aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/prototypes/waybar-redesign-prototype.html853
-rw-r--r--todo.org69
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-concept-01.pngbin0 -> 1639047 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-concept-02.pngbin0 -> 1611392 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-concept-03.pngbin0 -> 1351841 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-concept-04.pngbin0 -> 1631303 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-concept-05.pngbin0 -> 1157684 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-variant-sheet-01.pngbin0 -> 1717647 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-variant-sheet-02.pngbin0 -> 1828740 bytes
-rw-r--r--working/clock-display-references/2026-07-17-clock-strip-variant-sheet-03.pngbin0 -> 1828740 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-audemars-moonphase.pngbin0 -> 527486 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-blancpain-fifty-fathoms.pngbin0 -> 697946 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-breitling-navitimer.pngbin0 -> 301267 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-date-idea-detail.pngbin0 -> 29433 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-date-idea.pngbin0 -> 1819745 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-date-window-detail.pngbin0 -> 22156 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-iwc-portugieser.pngbin0 -> 192781 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-omega-speedmaster.pngbin0 -> 491327 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-panerai-luminor-marina.pngbin0 -> 128425 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-patek-moonphase.pngbin0 -> 532449 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-patek-perpetual-calendar.pngbin0 -> 198977 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-patek-two-register.pngbin0 -> 175575 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-reference-notes.txt20
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-rolex-cyclops-detail.pngbin0 -> 28722 bytes
-rw-r--r--working/clock-display-references/2026-07-18-chronograph-rolex-submariner-date.pngbin0 -> 492099 bytes
-rw-r--r--working/clock-display-references/README.org6
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black-detail.jpegbin0 -> 11473 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black.jpgbin0 -> 13108 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender-detail.jpegbin0 -> 12294 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender.webpbin0 -> 95620 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-detail.jpegbin0 -> 8710 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-pair.jpgbin0 -> 88208 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-style-sheet.jpegbin0 -> 4645 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-tone-volume.jpgbin0 -> 7654 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-reference.org65
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-source.js455
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-single-knob-black.webpbin0 -> 33262 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-single-knob-index-arrow.jpegbin0 -> 3283 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-split-flap-header.webpbin0 -> 44808 bytes
-rw-r--r--working/retro-stereo-widgets/references/2026-07-18-terminal-display.jpgbin0 -> 4522828 bytes
-rw-r--r--working/weather-kit-integration/README.org119
-rwxr-xr-xworking/weather-kit-integration/weather215
-rw-r--r--working/weather-kit-integration/weather-chip.html97
-rw-r--r--working/weather-kit-integration/weather-glyphs.svg81
44 files changed, 1980 insertions, 0 deletions
diff --git a/docs/prototypes/waybar-redesign-prototype.html b/docs/prototypes/waybar-redesign-prototype.html
new file mode 100644
index 0000000..2307b69
--- /dev/null
+++ b/docs/prototypes/waybar-redesign-prototype.html
@@ -0,0 +1,853 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Waybar redesign — six directions</title>
+<style>
+/* ============ kit tokens (from tokens.json) ============ */
+:root{
+ --ground:#151311; --panel:#100f0f; --well:#0a0c0d; --raise:#1a1917;
+ --silver:#bfc4d0; --cream:#f3e7c5; --steel:#969385; --dim:#7c838a;
+ --slate:#424f5e; --slate-hi:#54677d; --wash:#2c2f32;
+ --pass:#74932f; --fail:#cb6b4d; --phos:#7fe0a0; --phos-dim:#1c3626;
+ --vfd:#63e6c8; --sevgrn:#57d357; --sevred:#e2543f; --sevoff:#1a1613;
+ --jewel-r:#ff5b45; --jewel-a:#ffb43a; --jewel-g:#6fce33;
+ --gold:#e2a038; --gold-hi:#ffbe54; --amber-warn:#f0b552; --amber-edge:#7d5c16;
+ --mono:"BerkeleyMono Nerd Font","Berkeley Mono",monospace;
+ --chrome:linear-gradient(180deg,#f2f4f6 0%,#a9b1b8 26%,#62686d 48%,#cfd4da 72%,#7e858b 100%);
+ --chrome-soft:linear-gradient(180deg,#d9dde1 0%,#8f969c 40%,#565b60 55%,#aab0b6 100%);
+ --brush:repeating-linear-gradient(90deg,#c7cbd0 0 2px,#b2b7bd 2px 3px,#bfc4ca 3px 5px);
+}
+*{box-sizing:border-box;margin:0;padding:0}
+body{background:var(--ground);color:var(--silver);font-family:var(--mono);padding:34px 4vw 120px}
+h1{color:var(--cream);font-size:21px;letter-spacing:.04em;margin-bottom:6px}
+.sub{color:var(--steel);font-size:12.5px;max-width:900px;line-height:1.55;margin-bottom:8px}
+.swatches{display:flex;gap:6px;margin:14px 0 34px}
+.swatches i{width:22px;height:14px;border-radius:3px;border:1px solid #000}
+section.var{margin:0 0 58px}
+h2{font-size:14px;color:var(--gold-hi);letter-spacing:.12em;text-transform:uppercase;margin-bottom:2px}
+h2 .n{color:var(--steel);margin-right:8px}
+.cap{color:var(--steel);font-size:12px;line-height:1.55;max-width:980px;margin:6px 0 12px}
+.cap b{color:var(--silver);font-weight:600}
+/* true-proportion full-bar miniature */
+.minibar{width:100%;aspect-ratio:3440/54;min-height:26px;border-radius:999px;display:flex;align-items:center;
+ padding:0 1.1%;gap:.55%;font-size:9.5px;overflow:hidden;margin-bottom:14px}
+.minibar .sp{flex:1}
+.minibar span{white-space:nowrap;line-height:1}
+/* the large right-cluster stage */
+.stage{border-radius:14px;padding:26px 28px 22px;display:inline-flex;align-items:center;gap:18px;max-width:100%;overflow-x:auto}
+.lbl{font-size:8.5px;letter-spacing:.22em;text-transform:uppercase;text-align:center;margin-bottom:6px;white-space:nowrap}
+.mod{display:flex;flex-direction:column;align-items:center}
+.jewel{width:9px;height:9px;border-radius:50%;display:inline-block;border:1.5px solid #05050588;
+ box-shadow:inset 0 -2px 3px #0009, inset 0 1px 2px #fff5, 0 0 6px 0 currentColor}
+/* drum counters */
+.dgroup{display:flex;align-items:center;border-radius:11px;padding:5px 7px}
+.drum{min-width:26px;padding:7px 5px;text-align:center;font-size:19px;font-weight:700;color:#e9e9e9;
+ background:linear-gradient(180deg,#000 0%,#181818 12%,#3d3d3d 46%,#2b2b2b 60%,#0c0c0c 88%,#000 100%);
+ border-left:1px solid #000;border-right:1px solid #333;
+ text-shadow:0 1px 1px #000;line-height:1}
+.drum:first-child{border-radius:6px 0 0 6px}.drum:last-child{border-radius:0 6px 6px 0}
+.knurl{width:7px;align-self:stretch;border-radius:3px;margin:2px 1px;
+ background:repeating-linear-gradient(0deg,#101010 0 2px,#3c3c3c 2px 4px)}
+.sep-dot{width:7px;height:7px;border-radius:50%;background:radial-gradient(circle at 35% 30%,var(--gold-hi),#7d5c16 70%);margin:0 4px}
+/* knobs */
+.knob{width:30px;height:30px;border-radius:50%;
+ background:radial-gradient(circle at 34% 28%,#e8ecef,#9aa1a7 45%,#4d5257 78%,#31353a);
+ border:2px solid #1c1e20;position:relative;box-shadow:0 2px 5px #000a, inset 0 1px 1px #fff8}
+.knob::after{content:"";position:absolute;left:50%;top:3px;width:2px;height:8px;background:#17191b;transform:translateX(-50%);border-radius:1px}
+.knob.brass{background:radial-gradient(circle at 34% 28%,#ffdf9a,var(--gold) 45%,#8f671f 80%,#5c4212)}
+/* vfd window */
+.vfdwin{background:#031311;border-radius:6px;padding:7px 12px;color:var(--vfd);
+ box-shadow:inset 0 2px 8px #000, inset 0 0 24px #063a31, 0 0 1px #000;
+ text-shadow:0 0 7px #2ee6c266;font-size:18px;letter-spacing:.08em}
+svg{display:block}
+.note{margin-top:44px;border-top:1px solid #2a2724;padding-top:16px;color:var(--steel);font-size:12px;line-height:1.6;max-width:980px}
+.note b{color:var(--cream)}
+</style>
+</head>
+<body>
+
+<!-- ===== custom weather glyph set (stroke/fill = currentColor, recolour per module) =====
+ Live snapshot 2026-07-18: New Orleans, clear, 82°F (feels 92°F), humidity 88%, wind 6 WNW.
+ A real module maps the provider's condition code to one of these symbols; the sun is
+ the current reading. Drawn custom (not Nerd Font / emoji) so they sit in the kit. -->
+<svg width="0" height="0" style="position:absolute" aria-hidden="true"><defs>
+ <symbol id="wx-sun" viewBox="0 0 24 24">
+ <circle cx="12" cy="12" r="4.3" fill="currentColor"/>
+ <g stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
+ <line x1="12" y1="1.5" x2="12" y2="4.6"/><line x1="12" y1="19.4" x2="12" y2="22.5"/>
+ <line x1="1.5" y1="12" x2="4.6" y2="12"/><line x1="19.4" y1="12" x2="22.5" y2="12"/>
+ <line x1="4.6" y1="4.6" x2="6.8" y2="6.8"/><line x1="17.2" y1="17.2" x2="19.4" y2="19.4"/>
+ <line x1="4.6" y1="19.4" x2="6.8" y2="17.2"/><line x1="17.2" y1="6.8" x2="19.4" y2="4.6"/>
+ </g>
+ </symbol>
+ <symbol id="wx-partly" viewBox="0 0 24 24">
+ <circle cx="8.5" cy="8" r="3.2" fill="currentColor"/>
+ <g stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
+ <line x1="8.5" y1="1.5" x2="8.5" y2="3.4"/><line x1="1.7" y1="8" x2="3.6" y2="8"/>
+ <line x1="3.7" y1="3.2" x2="5" y2="4.5"/><line x1="13.3" y1="3.2" x2="12" y2="4.5"/>
+ </g>
+ <g fill="currentColor"><circle cx="12" cy="15" r="3.6"/><circle cx="16.5" cy="13" r="4.4"/><circle cx="20" cy="15.6" r="3.1"/><rect x="11" y="15" width="10" height="4" rx="2"/></g>
+ </symbol>
+ <symbol id="wx-cloud" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="13" r="4"/><circle cx="12.5" cy="10.5" r="5.2"/><circle cx="17" cy="13.5" r="3.6"/><rect x="6.5" y="13" width="11.5" height="4.2" rx="2.1"/></g>
+ </symbol>
+ <symbol id="wx-rain" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g>
+ <g stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><line x1="8" y1="15.5" x2="6.6" y2="19.5"/><line x1="12" y1="15.5" x2="10.6" y2="19.5"/><line x1="16" y1="15.5" x2="14.6" y2="19.5"/></g>
+ </symbol>
+ <symbol id="wx-storm" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g>
+ <path d="M12.5 14 L9 19.5 h3 L10.5 23.5 L15.5 17.5 h-3 L14 14 Z" fill="currentColor"/>
+ </symbol>
+ <symbol id="wx-snow" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g>
+ <g stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
+ <g transform="translate(9,17.5)"><line x1="-2" y1="0" x2="2" y2="0"/><line x1="0" y1="-2" x2="0" y2="2"/><line x1="-1.4" y1="-1.4" x2="1.4" y2="1.4"/><line x1="-1.4" y1="1.4" x2="1.4" y2="-1.4"/></g>
+ <g transform="translate(15,18.5)"><line x1="-2" y1="0" x2="2" y2="0"/><line x1="0" y1="-2" x2="0" y2="2"/><line x1="-1.4" y1="-1.4" x2="1.4" y2="1.4"/><line x1="-1.4" y1="1.4" x2="1.4" y2="-1.4"/></g>
+ </g>
+ </symbol>
+ <symbol id="wx-fog" viewBox="0 0 24 24">
+ <g stroke="currentColor" stroke-width="1.9" stroke-linecap="round">
+ <line x1="3" y1="7" x2="21" y2="7"/><line x1="5" y1="11.5" x2="19" y2="11.5"/><line x1="3" y1="16" x2="18" y2="16"/><line x1="7" y1="20.5" x2="21" y2="20.5"/>
+ </g>
+ </symbol>
+ <!-- wind vane: arrow points UP = north at 0deg; rotate by the source bearing (WNW = 293deg) -->
+ <symbol id="wx-vane" viewBox="0 0 24 24">
+ <path d="M12 2.4 L15.6 9 L12.9 9 L12.9 21 L11.1 21 L11.1 9 L8.4 9 Z" fill="currentColor" stroke="currentColor" stroke-width=".8" stroke-linejoin="round"/>
+ </symbol>
+ <!-- wind (windy condition): flowing gust lines, each ending in a curl -->
+ <symbol id="wx-wind" viewBox="0 0 24 24">
+ <g fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round">
+ <path d="M2.5 8 H13.5 a2.6 2.6 0 1 0 -2.55 -3.1"/>
+ <path d="M2.5 12.5 H18 a2.9 2.9 0 1 1 -2.85 3.45"/>
+ <path d="M2.5 17 H10.5 a2.3 2.3 0 1 1 -2.25 2.75"/>
+ </g>
+ </symbol>
+</defs></svg>
+
+<h1>Waybar redesign — six directions</h1>
+<p class="sub">Chronograph date/time as the anchor, whole-bar aesthetic around it. Each direction shows the full bar at true
+proportion (3440×54), then the right cluster enlarged. Left-side functionality is unchanged everywhere — only the skin varies.
+Right cluster everywhere: AUX (the future desktop-settings module) · network · audio · bluetooth · maint · timer · tray · <b>weather</b> · date · time.
+The <b>weather module is new</b>: current conditions for the location, drawn with a custom glyph set (below) rather than Nerd Font icons.
+The reading is a live snapshot — New Orleans, clear, 82°/feels 92°. All colours are kit tokens from the widget gallery.</p>
+<div class="swatches">
+ <i style="background:#100f0f" title="panel"></i><i style="background:#e2a038" title="gold"></i>
+ <i style="background:#f3e7c5" title="cream"></i><i style="background:#bfc4d0" title="silver"></i>
+ <i style="background:#63e6c8" title="vfd"></i><i style="background:#7fe0a0" title="phos"></i>
+ <i style="background:#ffb43a" title="jewel-a"></i><i style="background:#6fce33" title="jewel-g"></i>
+ <i style="background:#ff5b45" title="jewel-r"></i><i style="background:#e2543f" title="sevred"></i>
+</div>
+<div style="display:flex;gap:22px;align-items:center;flex-wrap:wrap;background:linear-gradient(180deg,#1a1917,#100f0f);border:1px solid #262320;border-radius:11px;padding:14px 20px;margin:0 0 34px">
+ <div style="color:var(--steel);font-size:11px;letter-spacing:.24em;text-transform:uppercase;line-height:1.5">Custom weather<br>glyph set</div>
+ <div style="display:flex;gap:20px;color:var(--cream)">
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--gold-hi)"><use href="#wx-sun"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">CLEAR</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--cream)"><use href="#wx-partly"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">PARTLY</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--silver)"><use href="#wx-cloud"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">CLOUD</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--slate-hi)"><use href="#wx-rain"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">RAIN</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--jewel-a)"><use href="#wx-storm"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">STORM</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--vfd)"><use href="#wx-snow"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">SNOW</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--dim)"><use href="#wx-fog"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">FOG</div></div>
+ <div style="text-align:center"><svg width="28" height="28" style="color:var(--silver)"><use href="#wx-wind"/></svg><div style="color:var(--steel);font-size:9px;letter-spacing:.1em;margin-top:4px">WINDY</div></div>
+ </div>
+</div>
+
+<!-- ================= W WEATHER WIDGET DIRECTIONS ================= -->
+<section class="var">
+<h2><span class="n">W</span>Weather widget — directions</h2>
+<p class="cap">Standalone takes on the weather widget, every one showing the same live reading (<b>New Orleans · clear · 82° / feels 92° · wind 6</b>). Same custom glyph set, six different instrument idioms. The bar modules below use the compact chip; the others are candidates for a fuller panel or a click-to-expand.
+<br><b>Wind collapses into one slot, constant width:</b> below ~15 mph it shows the vane + speed (direction is the interesting part when it's calm); at ~15 mph and up it flips to the gust glyph + speed (when it's windy, "windy" is the headline, not the compass point). The two minimal chips show the calm and windy states at identical footprint.</p>
+<div style="display:flex;flex-wrap:wrap;gap:30px 34px;align-items:flex-start;background:linear-gradient(180deg,#151413,#0e0d0d);border:1px solid #262320;border-radius:14px;padding:30px 30px 26px">
+
+ <!-- 1. compact chip -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <div style="display:flex;align-items:center;gap:9px;background:#080808;border-radius:9px;padding:8px 13px;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #33290f">
+ <svg width="30" height="30" style="color:var(--gold-hi)"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#e9e9e9;font-size:19px">82°</span><span style="color:var(--gold);font-size:8px;letter-spacing:.12em">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#33290f;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="20" height="20" style="color:var(--gold-hi);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05"><span style="color:#e9e9e9;font-size:12px">WNW</span><span style="color:var(--gold);font-size:8px">6 mph</span></div>
+ </div>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Compact chip</figcaption>
+ </figure>
+
+ <!-- 1b. minimal chip — symbol · degrees · direction-arrow · mph -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <div style="display:flex;align-items:center;gap:7px;background:#080808;border-radius:8px;padding:6px 11px;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #33290f">
+ <svg width="24" height="24" style="color:var(--gold-hi)"><use href="#wx-sun"/></svg>
+ <span style="color:#e9e9e9;font-size:17px">82°</span>
+ <svg width="17" height="17" style="color:var(--gold-hi);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <span style="color:var(--gold);font-size:13px">6</span>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Chip — calm · 6 mph</figcaption>
+ </figure>
+
+ <!-- 1c. minimal chip, windy state — gust glyph replaces the vane above the threshold -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <div style="display:flex;align-items:center;gap:7px;background:#080808;border-radius:8px;padding:6px 11px;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #33290f">
+ <svg width="24" height="24" style="color:var(--gold-hi)"><use href="#wx-sun"/></svg>
+ <span style="color:#e9e9e9;font-size:17px">82°</span>
+ <svg width="20" height="20" style="color:var(--gold-hi)"><use href="#wx-wind"/></svg>
+ <span style="color:var(--gold);font-size:13px">22</span>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Chip — windy · 22 mph</figcaption>
+ </figure>
+
+ <!-- 2. round instrument gauge -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <svg width="130" height="130" viewBox="0 0 130 130">
+ <circle cx="65" cy="65" r="62" fill="#100f0e" stroke="var(--amber-edge)" stroke-width="1.4"/>
+ <circle cx="65" cy="65" r="57" fill="#0c0b0a" stroke="#241c14" stroke-width="1"/>
+ <g stroke="var(--gold)" stroke-width="1.6">
+ <line x1="65" y1="13" x2="65" y2="7"/><line x1="106" y1="24" x2="101.8" y2="28.2"/>
+ <line x1="123" y1="65" x2="117" y2="65"/><line x1="106" y1="106" x2="101.8" y2="101.8"/>
+ <line x1="65" y1="123" x2="65" y2="117"/><line x1="24" y1="106" x2="28.2" y2="101.8"/>
+ <line x1="7" y1="65" x2="13" y2="65"/><line x1="24" y1="24" x2="28.2" y2="28.2"/>
+ </g>
+ <use href="#wx-sun" x="53" y="21" width="24" height="24" style="color:var(--amber-warn)"/>
+ <line x1="65" y1="65" x2="85" y2="31" stroke="var(--sevred)" stroke-width="2" stroke-linecap="round"/>
+ <line x1="65" y1="65" x2="59" y2="75" stroke="var(--sevred)" stroke-width="2" stroke-linecap="round"/>
+ <circle cx="65" cy="65" r="3.2" fill="#1c1c1c" stroke="#000"/>
+ <text x="65" y="90" text-anchor="middle" fill="var(--cream)" font-family="monospace" font-size="17">82°</text>
+ <g transform="rotate(293 55 106)"><use href="#wx-vane" x="49" y="100" width="12" height="12" style="color:var(--gold-hi)"/></g>
+ <text x="72" y="110" text-anchor="middle" fill="var(--steel)" font-family="monospace" font-size="9">WNW 6</text>
+ </svg>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Round gauge</figcaption>
+ </figure>
+
+ <!-- 3. split-flap board -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <div style="display:flex;flex-direction:column;align-items:center;gap:5px;background:#070707;border-radius:9px;padding:11px 13px;box-shadow:inset 0 0 16px #000, 0 0 0 1px #222">
+ <div style="display:flex;gap:4px;align-items:center">
+ <svg width="26" height="26" style="color:var(--cream)"><use href="#wx-sun"/></svg>
+ <span style="position:relative;display:inline-grid;place-items:center;width:22px;height:30px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:19px;border-radius:3px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000;box-shadow:0 -1px 0 #ffffff10"></span>8</span>
+ <span style="position:relative;display:inline-grid;place-items:center;width:22px;height:30px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:19px;border-radius:3px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000;box-shadow:0 -1px 0 #ffffff10"></span>2</span>
+ <span style="position:relative;display:inline-grid;place-items:center;width:16px;height:30px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:15px;border-radius:3px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000"></span>°</span>
+ </div>
+ <div style="display:flex;gap:2px">
+ <span style="position:relative;display:inline-grid;place-items:center;width:15px;height:21px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:12px;border-radius:2px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000"></span>C</span>
+ <span style="position:relative;display:inline-grid;place-items:center;width:15px;height:21px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:12px;border-radius:2px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000"></span>L</span>
+ <span style="position:relative;display:inline-grid;place-items:center;width:15px;height:21px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:12px;border-radius:2px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000"></span>E</span>
+ <span style="position:relative;display:inline-grid;place-items:center;width:15px;height:21px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:12px;border-radius:2px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000"></span>A</span>
+ <span style="position:relative;display:inline-grid;place-items:center;width:15px;height:21px;background:linear-gradient(180deg,#242424,#0b0b0b);color:var(--cream);font-size:12px;border-radius:2px;box-shadow:inset 0 0 0 1px #000"><span style="position:absolute;left:1px;right:1px;top:50%;height:1px;background:#000"></span>R</span>
+ </div>
+ <div style="display:flex;gap:5px;align-items:center;color:var(--steel);font-size:9px;letter-spacing:.08em;margin-top:1px">
+ <svg width="13" height="13" style="color:var(--cream);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>WNW · 6 MPH
+ </div>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Split-flap</figcaption>
+ </figure>
+
+ <!-- 4. VFD screen -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <div style="position:relative;overflow:hidden;min-width:158px;background:linear-gradient(180deg,#041210,#020a09);border:1px solid #0e3028;border-radius:8px;padding:12px 16px;box-shadow:inset 0 0 22px #063a31, inset 0 2px 8px #000">
+ <div style="display:flex;align-items:center;gap:11px;color:var(--vfd);text-shadow:0 0 8px #2ee6c2aa">
+ <svg width="30" height="30" style="color:var(--vfd)"><use href="#wx-sun"/></svg>
+ <div style="line-height:1.15"><div style="font-size:21px;letter-spacing:.05em">82°F</div><div style="font-size:10px;letter-spacing:.16em;opacity:.85">CLEAR</div></div>
+ </div>
+ <div style="margin-top:8px;display:flex;align-items:center;gap:6px;color:var(--vfd);text-shadow:0 0 8px #2ee6c2aa;font-size:11px;letter-spacing:.1em">
+ <svg width="14" height="14" style="color:var(--vfd);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>WIND WNW 6 MPH
+ </div>
+ <div style="position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent 0 2px,#00000030 2px 3px);pointer-events:none"></div>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">VFD screen</figcaption>
+ </figure>
+
+ <!-- 5. nixie tubes -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:11px">
+ <div style="display:flex;align-items:center;gap:11px;background:radial-gradient(120% 100% at 50% 0%,#171310,#0a0806);border-radius:9px;padding:10px 14px;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #241a10">
+ <svg width="30" height="30" style="color:#ff8a3a"><use href="#wx-sun"/></svg>
+ <div style="display:flex;gap:4px">
+ <span style="display:inline-grid;place-items:center;width:25px;height:37px;border-radius:5px;background:radial-gradient(circle at 50% 38%,#2c1708,#0a0604);border:1px solid #1c0f06;color:#ff8a3a;font-size:23px;text-shadow:0 0 11px #ff6a1ecc, 0 0 4px #ffb070">8</span>
+ <span style="display:inline-grid;place-items:center;width:25px;height:37px;border-radius:5px;background:radial-gradient(circle at 50% 38%,#2c1708,#0a0604);border:1px solid #1c0f06;color:#ff8a3a;font-size:23px;text-shadow:0 0 11px #ff6a1ecc, 0 0 4px #ffb070">2</span>
+ <span style="display:inline-grid;place-items:center;width:18px;height:37px;border-radius:5px;background:radial-gradient(circle at 50% 38%,#2c1708,#0a0604);border:1px solid #1c0f06;color:#ff8a3a;font-size:16px;text-shadow:0 0 10px #ff6a1ecc">°</span>
+ </div>
+ <div style="display:flex;flex-direction:column;align-items:center;gap:2px;color:#ff8a3a;text-shadow:0 0 8px #ff6a1e99">
+ <svg width="18" height="18" style="color:#ff8a3a;transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <span style="font-size:8px;letter-spacing:.06em">WNW 6</span>
+ </div>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Nixie</figcaption>
+ </figure>
+
+ <!-- 6. barometer-style condition dial -->
+ <figure style="margin:0;display:flex;flex-direction:column;align-items:center;gap:9px">
+ <svg width="140" height="140" viewBox="0 0 140 140">
+ <circle cx="70" cy="70" r="66" fill="#100f0e" stroke="var(--gold)" stroke-width="1.4"/>
+ <circle cx="70" cy="70" r="61" fill="#0b0a09" stroke="#241c14" stroke-width="1"/>
+ <circle cx="70" cy="70" r="52" fill="none" stroke="#241c14" stroke-width=".8" stroke-dasharray="1.5 3.4"/>
+ <use href="#wx-sun" x="60" y="8" width="20" height="20" style="color:var(--gold-hi)"/>
+ <use href="#wx-partly" x="100.7" y="27.6" width="20" height="20" style="color:#5a5650"/>
+ <use href="#wx-cloud" x="110.7" y="71.6" width="20" height="20" style="color:#5a5650"/>
+ <use href="#wx-rain" x="82.6" y="106.8" width="20" height="20" style="color:#5a5650"/>
+ <use href="#wx-storm" x="37.4" y="106.8" width="20" height="20" style="color:#5a5650"/>
+ <use href="#wx-snow" x="9.3" y="71.6" width="20" height="20" style="color:#5a5650"/>
+ <use href="#wx-fog" x="19.3" y="27.6" width="20" height="20" style="color:#5a5650"/>
+ <line x1="70" y1="70" x2="70" y2="26" stroke="var(--gold-hi)" stroke-width="2.4" stroke-linecap="round"/>
+ <line x1="70" y1="70" x2="70" y2="80" stroke="var(--gold-hi)" stroke-width="2.4" stroke-linecap="round"/>
+ <circle cx="70" cy="70" r="3.4" fill="var(--gold)" stroke="#000"/>
+ </svg>
+ <div style="display:flex;align-items:center;gap:8px;color:var(--cream);font-family:var(--mono);font-size:13px">
+ 82°F<span style="color:#3a3630">·</span>
+ <svg width="14" height="14" style="color:var(--gold-hi);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <span style="color:var(--steel)">WNW 6</span>
+ </div>
+ <figcaption style="color:var(--steel);font-size:10px;letter-spacing:.16em;text-transform:uppercase">Condition dial</figcaption>
+ </figure>
+
+</div>
+</section>
+
+<!-- ================= V1 GOLD PINSTRIPE ODOMETER ================= -->
+<section class="var">
+<h2><span class="n">1</span>Gold-pinstripe odometer console</h2>
+<div class="minibar" style="background:#0c0b0b;border:1px solid var(--amber-edge);box-shadow:0 0 0 2px #000,inset 0 0 0 1.5px #00000000, inset 0 0 18px #000;outline:1px solid #3a2c10;outline-offset:-4px">
+ <span style="color:var(--steel)">▶ ✦</span><span style="color:var(--gold-hi)">1 2 3 4 5</span>
+ <span style="color:var(--steel)">▦ emacs — widgets.js</span><span class="sp"></span>
+ <span style="color:var(--steel)">AUX ᯤ ♪ ᛒ ⛭ ⏱ ⋮⋮</span>
+ <span style="color:#e9e9e9;background:#000;border-radius:3px;padding:1px 4px">SAT JUL 18</span>
+ <span style="color:#e9e9e9;background:#000;border-radius:3px;padding:1px 4px">07:55 PM CDT</span>
+</div>
+<div class="stage" style="background:linear-gradient(180deg,#141212,#0d0c0c);border:1px solid var(--amber-edge);box-shadow:inset 0 0 0 3px #060505, inset 0 0 0 4px #4a3814, inset 0 1px 14px #000, 0 4px 18px #000">
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Aux</div><div class="knob brass"></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Net</div>
+ <div style="display:flex;gap:3px;align-items:flex-end;height:26px;padding-top:4px">
+ <i style="width:5px;height:8px;background:var(--gold)"></i><i style="width:5px;height:13px;background:var(--gold)"></i>
+ <i style="width:5px;height:18px;background:var(--gold-hi)"></i><i style="width:5px;height:23px;background:#3a332a"></i>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Volume</div>
+ <svg width="52" height="30" viewBox="0 0 52 30">
+ <path d="M4 28 A25 25 0 0 1 48 28" fill="none" stroke="#3a332a" stroke-width="3"/>
+ <path d="M4 28 A25 25 0 0 1 34 7.5" fill="none" stroke="var(--gold)" stroke-width="3"/>
+ <line x1="26" y1="28" x2="37" y2="8" stroke="var(--sevred)" stroke-width="2"/>
+ <circle cx="26" cy="28" r="3" fill="#2b2b2b" stroke="#000"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Bluetooth</div>
+ <div style="display:flex;gap:7px;align-items:center;padding-top:5px">
+ <span style="color:var(--silver);font-size:17px">ᛒ</span><span class="jewel" style="color:var(--jewel-g);background:var(--jewel-g)"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Maint</div>
+ <div style="display:flex;gap:7px;align-items:center;padding-top:5px">
+ <span style="color:var(--silver);font-size:16px">⛭</span><span class="jewel" style="color:var(--jewel-a);background:var(--jewel-a)"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Timer</div>
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #33290f">
+ <div class="drum" style="font-size:15px;min-width:20px;padding:5px 4px">12</div>
+ <div class="drum" style="font-size:15px;min-width:20px;padding:5px 4px">34</div>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Tray</div>
+ <div style="display:flex;gap:6px;padding-top:9px"><i style="width:6px;height:6px;border-radius:50%;background:#3f3a32"></i><i style="width:6px;height:6px;border-radius:50%;background:#3f3a32"></i><i style="width:6px;height:6px;border-radius:50%;background:#3f3a32"></i></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Weather</div>
+ <div style="display:flex;align-items:center;gap:9px;background:#080808;border-radius:9px;padding:6px 11px;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #33290f">
+ <svg width="28" height="28" style="color:var(--gold-hi)"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#e9e9e9;font-size:17px">82°</span>
+ <span style="color:var(--gold);font-size:8px;letter-spacing:.12em">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#33290f;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="18" height="18" style="color:var(--gold-hi);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#e9e9e9;font-size:11px;letter-spacing:.06em">WNW</span>
+ <span style="color:var(--gold);font-size:8px">6 mph</span>
+ </div>
+ </div></div></div>
+ <div style="width:1px;align-self:stretch;background:linear-gradient(180deg,transparent,var(--amber-edge),transparent)"></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold-hi)">Day&nbsp;&nbsp;·&nbsp;&nbsp;Month&nbsp;&nbsp;·&nbsp;&nbsp;Date</div>
+ <div style="display:flex;align-items:center;gap:10px">
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #33290f, 0 1px 0 #ffffff10">
+ <div class="knurl"></div><div class="drum">S</div><div class="drum">A</div><div class="drum">T</div><div class="knurl"></div>
+ </div>
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #33290f">
+ <div class="knurl"></div><div class="drum">J</div><div class="drum">U</div><div class="drum">L</div><div class="knurl"></div>
+ </div>
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #33290f">
+ <div class="knurl"></div><div class="drum">1</div><div class="drum">8</div><div class="knurl"></div>
+ </div>
+ </div></div>
+ <div class="sep-dot"></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold-hi)">Hour&nbsp;·&nbsp;Min&nbsp;·&nbsp;AM/PM&nbsp;·&nbsp;TZ</div>
+ <div style="display:flex;align-items:center;gap:10px">
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #33290f">
+ <div class="knurl"></div><div class="drum">0</div><div class="drum">7</div>
+ <div style="color:#777;font-size:17px;padding:0 2px">:</div>
+ <div class="drum">5</div><div class="drum">5</div><div class="knurl"></div>
+ </div>
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #33290f">
+ <div class="drum" style="font-size:15px">P</div><div class="drum" style="font-size:15px">M</div>
+ </div>
+ <div class="dgroup" style="background:#080808;box-shadow:inset 0 2px 10px #000, 0 0 0 1px #33290f">
+ <div class="drum" style="font-size:15px">C</div><div class="drum" style="font-size:15px">D</div><div class="drum" style="font-size:15px">T</div>
+ </div>
+ </div></div>
+</div>
+<p class="cap"><b>Your 11:46 concept, extended to the whole bar.</b> One continuous black panel with the double gold pinstripe;
+engraved gold labels over every module so the bar reads as a single machined faceplate. Date and time are drum groups with
+knurled end-caps; the timer is a smaller drum pair; volume is a needle meter; statuses are jewel lamps. Warmest and most
+unified of the six — the pinstripe frames the entire bar, not each module.</p>
+</section>
+
+<!-- ================= V2 CHROME BEZEL CONSOLE ================= -->
+<section class="var">
+<h2><span class="n">2</span>Chrome-bezel instrument row</h2>
+<div class="minibar" style="background:#111010;border:1px solid #2e3134;box-shadow:inset 0 1px 8px #000">
+ <span style="color:var(--steel)">▶ ✦ 1 <b style="color:var(--cream)">2</b> 3 4 5 ▦ emacs — widgets.js</span><span class="sp"></span>
+ <span style="color:var(--steel)">AUX ᯤ ♪ ᛒ ⛭ ⏱ ⋮⋮</span>
+ <span style="color:#e9e9e9;background:#000;border:1px solid #888;border-radius:4px;padding:1px 4px">SAT JUL 18</span>
+ <span style="color:#e9e9e9;background:#000;border:1px solid #888;border-radius:4px;padding:1px 4px">07:55 PM</span>
+</div>
+<div class="stage" style="background:linear-gradient(180deg,#171515,#0e0d0d 60%,#131211);border:1px solid #2c2e30;box-shadow:inset 0 1px 10px #000, 0 4px 18px #000">
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Aux</div><div class="knob"></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Net</div>
+ <div style="border-radius:9px;padding:3px;background:var(--chrome-soft);box-shadow:0 2px 5px #000a">
+ <div style="background:#050607;border-radius:6px;padding:5px 9px;color:var(--vfd);font-size:14px;text-shadow:0 0 6px #2ee6c255">▂▄▆█</div>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Volume</div>
+ <div style="border-radius:9px;padding:3px;background:var(--chrome-soft);box-shadow:0 2px 5px #000a">
+ <svg width="56" height="26" viewBox="0 0 56 26" style="background:#0a0a09;border-radius:6px">
+ <path d="M6 22 A26 26 0 0 1 50 22" fill="none" stroke="#2c2a24" stroke-width="2.5"/>
+ <path d="M6 22 A26 26 0 0 1 38 5.5" fill="none" stroke="var(--cream)" stroke-width="2.5"/>
+ <line x1="28" y1="23" x2="40" y2="6" stroke="var(--sevred)" stroke-width="1.8"/>
+ </svg></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Blue</div>
+ <div style="border-radius:9px;padding:3px;background:var(--chrome-soft);box-shadow:0 2px 5px #000a">
+ <div style="background:#050607;border-radius:6px;padding:4px 8px;display:flex;gap:6px;align-items:center">
+ <span style="color:var(--silver);font-size:15px">ᛒ</span><span class="jewel" style="color:var(--vfd);background:var(--vfd);width:7px;height:7px"></span>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Maint</div>
+ <div style="border-radius:9px;padding:3px;background:var(--chrome-soft);box-shadow:0 2px 5px #000a">
+ <div style="background:#050607;border-radius:6px;padding:4px 8px;display:flex;gap:6px;align-items:center">
+ <span style="color:var(--silver);font-size:14px">⛭</span><span class="jewel" style="color:var(--jewel-g);background:var(--jewel-g);width:7px;height:7px"></span>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Timer</div>
+ <div style="border-radius:9px;padding:3px;background:var(--chrome-soft);box-shadow:0 2px 5px #000a">
+ <div style="background:#050607;border-radius:6px;padding:5px 8px;color:var(--cream);font-size:14px">--:--</div>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Tray</div>
+ <div style="display:flex;gap:6px;padding-top:9px"><i style="width:6px;height:6px;border-radius:50%;background:#43464a"></i><i style="width:6px;height:6px;border-radius:50%;background:#43464a"></i></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--steel)">Weather</div>
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div style="display:flex;align-items:center;gap:9px;background:#000;border-radius:7px;padding:6px 10px">
+ <svg width="26" height="26" style="color:var(--cream)"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:var(--cream);font-size:16px">82°</span>
+ <span style="color:var(--steel);font-size:8px;letter-spacing:.1em">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#2a2a2a;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="18" height="18" style="color:var(--cream);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:var(--cream);font-size:11px;letter-spacing:.06em">WNW</span>
+ <span style="color:var(--steel);font-size:8px">6 mph</span>
+ </div>
+ </div></div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--silver)">Sat · Jul · 18</div>
+ <div style="display:flex;gap:8px">
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum">S</div><div class="drum">A</div><div class="drum">T</div></div></div>
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum">J</div><div class="drum">U</div><div class="drum">L</div></div></div>
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum">1</div><div class="drum">8</div></div></div>
+ </div>
+ <div style="width:60%;height:2px;background:var(--vfd);opacity:.55;border-radius:2px;margin-top:5px;box-shadow:0 0 6px var(--vfd)"></div>
+ </div>
+ <div class="mod"><div class="lbl" style="color:var(--silver)">07 : 55 · PM · CDT</div>
+ <div style="display:flex;gap:8px">
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum">0</div><div class="drum">7</div>
+ <div style="color:#888;font-size:17px;padding:0 2px">:</div>
+ <div class="drum">5</div><div class="drum">5</div></div></div>
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum" style="font-size:15px">P</div><div class="drum" style="font-size:15px">M</div></div></div>
+ <div style="border-radius:11px;padding:4px;background:var(--chrome);box-shadow:0 3px 7px #000c, inset 0 1px 1px #fff">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum" style="font-size:15px">C</div><div class="drum" style="font-size:15px">D</div><div class="drum" style="font-size:15px">T</div></div></div>
+ </div>
+ <div style="width:60%;height:2px;background:var(--vfd);opacity:.55;border-radius:2px;margin-top:5px;box-shadow:0 0 6px var(--vfd)"></div>
+ </div>
+</div>
+<p class="cap"><b>Your first concept sheet, with every module earning a chrome bezel.</b> Individual polished frames on a
+leather-black ground; silver engraved labels; the lume underline (vfd family — Super-LumiNova blue-green) marks the two
+time modules as the "hands" of the bar. Cooler and more mechanical than V1: each module is its own instrument, the way
+the drum-variation sheet grouped them. Net becomes a VFD signal ladder to put one lit window on the bar's left half.</p>
+</section>
+
+<!-- ================= V3 TOOL WATCH ================= -->
+<section class="var">
+<h2><span class="n">3</span>Tool-watch dial (Speedmaster steel)</h2>
+<div class="minibar" style="background:var(--brush);border:1px solid #55595e;box-shadow:inset 0 -2px 6px #0008">
+ <span style="color:#26282a">▶ ✦ 1 <b>2</b> 3 4 5 ▦ emacs — widgets.js</span><span class="sp"></span>
+ <span style="color:#26282a">AUX ᯤ ♪ ᛒ ⛭ ⏱ ⋮⋮</span>
+ <span style="color:#e9e9e9;background:#0b0b0b;border-radius:3px;padding:1px 5px">SAT 18 · 19:55</span>
+</div>
+<div class="stage" style="background:var(--brush);border-radius:16px;border:1px solid #595e63;box-shadow:inset 0 1px 1px #fff8, inset 0 -3px 8px #0007, 0 5px 18px #000">
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Aux</div><div class="knob"></div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Net</div>
+ <div style="background:#0c0d0d;border-radius:8px;padding:6px 9px;box-shadow:inset 0 2px 6px #000, 0 1px 0 #fff6">
+ <div style="display:flex;gap:3px;align-items:flex-end;height:18px">
+ <i style="width:4px;height:6px;background:#e9e9e9"></i><i style="width:4px;height:10px;background:#e9e9e9"></i>
+ <i style="width:4px;height:14px;background:#e9e9e9"></i><i style="width:4px;height:18px;background:#3a3d3f"></i>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Volume</div>
+ <svg width="46" height="46" viewBox="0 0 46 46">
+ <circle cx="23" cy="23" r="21" fill="#0c0d0d" stroke="#585d62" stroke-width="1.6"/>
+ <g stroke="#e9e9e9" stroke-width="1.4">
+ <line x1="23" y1="4" x2="23" y2="9"/><line x1="42" y1="23" x2="37" y2="23"/>
+ <line x1="23" y1="42" x2="23" y2="37"/><line x1="4" y1="23" x2="9" y2="23"/>
+ </g>
+ <line x1="23" y1="23" x2="33" y2="10" stroke="#e9e9e9" stroke-width="2"/>
+ <circle cx="23" cy="23" r="2.2" fill="#e9e9e9"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Blue</div>
+ <div style="background:#0c0d0d;border-radius:8px;padding:6px 8px;display:flex;gap:6px;align-items:center;box-shadow:inset 0 2px 6px #000, 0 1px 0 #fff6">
+ <span style="color:#e9e9e9;font-size:15px">ᛒ</span><span class="jewel" style="color:var(--phos);background:var(--phos);width:7px;height:7px"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Maint</div>
+ <div style="background:#0c0d0d;border-radius:8px;padding:6px 8px;display:flex;gap:6px;align-items:center;box-shadow:inset 0 2px 6px #000, 0 1px 0 #fff6">
+ <span style="color:#e9e9e9;font-size:14px">⛭</span><span class="jewel" style="color:var(--jewel-a);background:var(--jewel-a);width:7px;height:7px"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Timer 30-min register</div>
+ <svg width="46" height="46" viewBox="0 0 46 46">
+ <circle cx="23" cy="23" r="21" fill="#0c0d0d" stroke="#585d62" stroke-width="1.6"/>
+ <g stroke="#9aa0a5" stroke-width="1">
+ <line x1="23" y1="5" x2="23" y2="10"/><line x1="38.6" y1="14" x2="34.3" y2="16.5"/>
+ <line x1="38.6" y1="32" x2="34.3" y2="29.5"/><line x1="23" y1="41" x2="23" y2="36"/>
+ <line x1="7.4" y1="32" x2="11.7" y2="29.5"/><line x1="7.4" y1="14" x2="11.7" y2="16.5"/>
+ </g>
+ <line x1="23" y1="23" x2="29" y2="9" stroke="var(--sevred)" stroke-width="1.8"/>
+ <circle cx="23" cy="23" r="2" fill="#e9e9e9"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Tray</div>
+ <div style="display:flex;gap:6px;padding-top:9px"><i style="width:6px;height:6px;border-radius:50%;background:#53585d"></i><i style="width:6px;height:6px;border-radius:50%;background:#53585d"></i><i style="width:6px;height:6px;border-radius:50%;background:#53585d"></i></div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Weather</div>
+ <div style="background:#0c0d0d;border-radius:8px;padding:6px 11px;display:flex;align-items:center;gap:9px;box-shadow:inset 0 2px 6px #000, 0 1px 0 #fff6">
+ <svg width="26" height="26" style="color:#e9e9e9"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#e9e9e9;font-size:16px">82°</span>
+ <span style="color:#8b9095;font-size:8px;letter-spacing:.1em">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#2a2c2e;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="18" height="18" style="color:#e9e9e9;transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#e9e9e9;font-size:11px;letter-spacing:.06em">WNW</span>
+ <span style="color:#8b9095;font-size:8px">6 mph</span>
+ </div>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Date aperture</div>
+ <div style="background:#0c0d0d;border-radius:9px;padding:8px 10px;display:flex;gap:8px;align-items:center;box-shadow:inset 0 2px 8px #000, 0 1px 0 #fff6">
+ <span style="color:#e9e9e9;letter-spacing:.14em;font-size:15px">SAT</span>
+ <div style="background:#f2f2ee;color:#111;border-radius:3px;padding:2px 7px;font-size:17px;font-weight:700;border:1.5px solid #63686d;box-shadow:inset 0 -2px 3px #0003">18</div>
+ <span style="color:#9aa0a5;letter-spacing:.14em;font-size:13px">JUL</span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:#2b2d2f">Time dial</div>
+ <div style="display:flex;align-items:center;gap:10px">
+ <svg width="62" height="62" viewBox="0 0 62 62">
+ <circle cx="31" cy="31" r="29" fill="#0c0d0d" stroke="#63686d" stroke-width="2"/>
+ <g stroke="#e9e9e9" stroke-width="2">
+ <line x1="31" y1="5" x2="31" y2="12"/><line x1="57" y1="31" x2="50" y2="31"/>
+ <line x1="31" y1="57" x2="31" y2="50"/><line x1="5" y1="31" x2="12" y2="31"/>
+ </g>
+ <g stroke="#8b9095" stroke-width="1.2">
+ <line x1="44" y1="8.5" x2="40.5" y2="14.6"/><line x1="53.5" y1="18" x2="47.4" y2="21.5"/>
+ <line x1="53.5" y1="44" x2="47.4" y2="40.5"/><line x1="44" y1="53.5" x2="40.5" y2="47.4"/>
+ <line x1="18" y1="53.5" x2="21.5" y2="47.4"/><line x1="8.5" y1="44" x2="14.6" y2="40.5"/>
+ <line x1="8.5" y1="18" x2="14.6" y2="21.5"/><line x1="18" y1="8.5" x2="21.5" y2="14.6"/>
+ </g>
+ <g fill="var(--phos)">
+ <circle cx="31" cy="14" r="1.5"/><circle cx="48" cy="31" r="1.5"/><circle cx="31" cy="48" r="1.5"/><circle cx="14" cy="31" r="1.5"/>
+ </g>
+ <line x1="31" y1="31" x2="20" y2="21" stroke="#e9e9e9" stroke-width="3.4" stroke-linecap="round"/>
+ <line x1="31" y1="31" x2="42" y2="13" stroke="#e9e9e9" stroke-width="2.2" stroke-linecap="round"/>
+ <line x1="31" y1="31" x2="26" y2="45" stroke="var(--sevred)" stroke-width="1.4" stroke-linecap="round"/>
+ <circle cx="31" cy="31" r="2.6" fill="#e9e9e9"/>
+ </svg>
+ <div style="background:#0c0d0d;border-radius:8px;padding:6px 9px;color:#e9e9e9;font-size:16px;box-shadow:inset 0 2px 6px #000, 0 1px 0 #fff6">19:55<span style="color:#8b9095;font-size:11px"> CDT</span></div>
+ </div></div>
+</div>
+<p class="cap"><b>The Speedmaster direction: brushed-steel bar, black dial insets, lume dots.</b> Time is an actual analog
+dial with baton hands plus a small digital repeater; the date is a framed white aperture (the Submariner cyclops date, flattened).
+Audio and timer become chronograph sub-registers — the timer literally is a 30-minute register. The steel ground makes this
+the lightest bar of the six; icons engrave dark into the metal rather than glowing.</p>
+</section>
+
+<!-- ================= V4 NAVITIMER ================= -->
+<section class="var">
+<h2><span class="n">4</span>Navitimer registers (cream on bronze)</h2>
+<div class="minibar" style="background:linear-gradient(180deg,#2b2119,#1c1611);border:1px solid #4a3a28;box-shadow:inset 0 1px 6px #000">
+ <span style="color:#c9b795">▶ ✦ 1 <b style="color:var(--cream)">2</b> 3 4 5 ▦ emacs — widgets.js</span><span class="sp"></span>
+ <span style="color:#c9b795">AUX ᯤ ♪ ᛒ ⛭ ⏱ ⋮⋮</span>
+ <span style="background:var(--cream);color:#1c1611;border-radius:3px;padding:1px 5px">SAT JUL 18 · 07:55 PM</span>
+</div>
+<div class="stage" style="background:linear-gradient(180deg,#2e231a,#1e1812 65%,#241c14);border-radius:16px;border:1px solid #574430;box-shadow:inset 0 1px 2px #ffedc411, inset 0 -4px 12px #000, 0 5px 18px #000">
+ <div class="mod"><div class="lbl" style="color:#c9b795">Aux</div><div class="knob brass"></div></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Net · slide rule</div>
+ <div style="background:var(--cream);border-radius:7px;padding:5px 8px;box-shadow:inset 0 -2px 4px #0004, 0 2px 5px #000a">
+ <svg width="58" height="16" viewBox="0 0 58 16">
+ <g stroke="#241c14" stroke-width="1"><line x1="3" y1="2" x2="3" y2="14"/><line x1="12" y1="5" x2="12" y2="14"/><line x1="21" y1="2" x2="21" y2="14"/><line x1="30" y1="5" x2="30" y2="14"/><line x1="39" y1="2" x2="39" y2="14"/><line x1="48" y1="5" x2="48" y2="14"/><line x1="55" y1="2" x2="55" y2="14"/></g>
+ <line x1="33" y1="0" x2="33" y2="16" stroke="var(--sevred)" stroke-width="1.6"/>
+ </svg></div></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Volume</div>
+ <svg width="48" height="48" viewBox="0 0 48 48">
+ <circle cx="24" cy="24" r="22" fill="var(--cream)" stroke="#574430" stroke-width="1.6"/>
+ <g stroke="#241c14" stroke-width="1.2">
+ <line x1="24" y1="4" x2="24" y2="10"/><line x1="44" y1="24" x2="38" y2="24"/>
+ <line x1="24" y1="44" x2="24" y2="38"/><line x1="4" y1="24" x2="10" y2="24"/>
+ </g>
+ <line x1="24" y1="24" x2="34" y2="11" stroke="#241c14" stroke-width="2"/>
+ <circle cx="24" cy="24" r="2" fill="#241c14"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Blue</div>
+ <svg width="34" height="34" viewBox="0 0 34 34">
+ <circle cx="17" cy="17" r="15" fill="var(--cream)" stroke="#574430" stroke-width="1.4"/>
+ <text x="17" y="22" text-anchor="middle" fill="#241c14" font-size="14" font-family="monospace">ᛒ</text>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Maint</div>
+ <svg width="34" height="34" viewBox="0 0 34 34">
+ <circle cx="17" cy="17" r="15" fill="var(--cream)" stroke="#574430" stroke-width="1.4"/>
+ <text x="17" y="22" text-anchor="middle" fill="#241c14" font-size="13" font-family="monospace">⛭</text>
+ <circle cx="26" cy="8" r="3" fill="var(--jewel-a)" stroke="#241c14" stroke-width=".8"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Timer</div>
+ <svg width="48" height="48" viewBox="0 0 48 48">
+ <circle cx="24" cy="24" r="22" fill="var(--cream)" stroke="#574430" stroke-width="1.6"/>
+ <g stroke="#241c14" stroke-width="1">
+ <line x1="24" y1="4" x2="24" y2="9"/><line x1="41.3" y1="14" x2="37" y2="16.5"/>
+ <line x1="41.3" y1="34" x2="37" y2="31.5"/><line x1="24" y1="44" x2="24" y2="39"/>
+ <line x1="6.7" y1="34" x2="11" y2="31.5"/><line x1="6.7" y1="14" x2="11" y2="16.5"/>
+ </g>
+ <line x1="24" y1="24" x2="31" y2="10" stroke="var(--sevred)" stroke-width="1.8"/>
+ <circle cx="24" cy="24" r="2" fill="#241c14"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Tray</div>
+ <div style="display:flex;gap:6px;padding-top:9px"><i style="width:6px;height:6px;border-radius:50%;background:#574430"></i><i style="width:6px;height:6px;border-radius:50%;background:#574430"></i></div></div>
+ <div class="mod"><div class="lbl" style="color:#c9b795">Weather</div>
+ <div style="background:var(--cream);border-radius:9px;padding:6px 11px;display:flex;align-items:center;gap:9px;box-shadow:inset 0 -2px 4px #0004, 0 2px 5px #000a">
+ <svg width="26" height="26" style="color:#241c14"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#241c14;font-size:16px;font-weight:700">82°</span>
+ <span style="color:#574430;font-size:8px;letter-spacing:.1em">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#b8ac8c;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="18" height="18" style="color:#241c14;transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:#241c14;font-size:11px;letter-spacing:.06em">WNW</span>
+ <span style="color:#574430;font-size:8px">6 mph</span>
+ </div>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--cream)">Date · beads-of-rice frame</div>
+ <div style="display:flex;align-items:center;gap:8px;background:#170f09;border-radius:9px;padding:7px 12px;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #574430">
+ <span style="color:var(--cream);letter-spacing:.12em;font-size:15px">SAT</span>
+ <div style="background:var(--cream);color:#170f09;border-radius:3px;padding:2px 7px;font-size:17px;font-weight:700;box-shadow:inset 0 -2px 3px #0004">18</div>
+ <span style="color:#c9b795;letter-spacing:.12em;font-size:13px">JUL</span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--cream)">Time · big register</div>
+ <div style="display:flex;align-items:center;gap:10px">
+ <svg width="60" height="60" viewBox="0 0 60 60">
+ <circle cx="30" cy="30" r="28" fill="var(--cream)" stroke="#574430" stroke-width="2"/>
+ <circle cx="30" cy="30" r="21" fill="none" stroke="#241c14" stroke-width=".8" stroke-dasharray="1.5 3.2"/>
+ <g fill="#241c14" font-size="8" font-family="monospace" text-anchor="middle">
+ <text x="30" y="12">12</text><text x="49" y="33">3</text><text x="30" y="54">6</text><text x="11" y="33">9</text>
+ </g>
+ <line x1="30" y1="30" x2="20" y2="20" stroke="#241c14" stroke-width="3" stroke-linecap="round"/>
+ <line x1="30" y1="30" x2="41" y2="13" stroke="#241c14" stroke-width="2" stroke-linecap="round"/>
+ <line x1="30" y1="30" x2="26" y2="44" stroke="var(--sevred)" stroke-width="1.3" stroke-linecap="round"/>
+ <circle cx="30" cy="30" r="2.4" fill="#241c14"/>
+ </svg>
+ <div style="background:#170f09;border-radius:8px;padding:6px 9px;color:var(--cream);font-size:16px;box-shadow:inset 0 2px 6px #000, 0 0 0 1px #574430">07:55<span style="color:#c9b795;font-size:11px"> PM</span></div>
+ </div></div>
+</div>
+<p class="cap"><b>The Navitimer direction: cream registers on a dark bronze dial.</b> Every instrument is a cream sub-dial with
+black markings — the inverse polarity of every other variation, so statuses read as printed gauge faces, not glowing lamps.
+Net is a slide-rule strip with the red index needle; the date sits in a dark framed aperture like the Navitimer's 3-o'clock
+window. Warm, aviation-flavored, and the busiest of the six — closest to a real cockpit cluster.</p>
+</section>
+
+<!-- ================= V5 DASHBOARD ================= -->
+<section class="var">
+<h2><span class="n">5</span>Auto dashboard (gauges + tell-tales + odometer)</h2>
+<div class="minibar" style="background:linear-gradient(180deg,#191715,#0f0e0d);border-top:2px solid #8a8f94;border-bottom:2px solid #6a6f74;box-shadow:inset 0 0 12px #000">
+ <span style="color:var(--steel)">▶ ✦ <b style="color:var(--jewel-a)">1 2 3 4 5</b> ▦ emacs — widgets.js</span><span class="sp"></span>
+ <span style="color:var(--jewel-a)">AUX NET VOL BT SVC ⏱ ⋮⋮</span>
+ <span style="color:#e9e9e9;background:#000;border-radius:3px;padding:1px 4px">SAT 18 JUL · 07:55 PM</span>
+</div>
+<div class="stage" style="background:linear-gradient(180deg,#1b1917,#100f0e 70%);border-radius:16px;border-top:3px solid #90959a;border-bottom:3px solid #5e6368;box-shadow:inset 0 0 18px #000, 0 5px 18px #000">
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Aux</div><div class="knob"></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Net signal</div>
+ <svg width="52" height="52" viewBox="0 0 52 52">
+ <circle cx="26" cy="26" r="24" fill="#0d0c0b" stroke="#90959a" stroke-width="2.4"/>
+ <g stroke="var(--jewel-a)" stroke-width="1.3" opacity=".9">
+ <line x1="26" y1="6" x2="26" y2="12"/><line x1="43" y1="13" x2="38.5" y2="17"/>
+ <line x1="46" y1="26" x2="40" y2="26"/><line x1="9" y1="13" x2="13.5" y2="17"/><line x1="6" y1="26" x2="12" y2="26"/>
+ </g>
+ <text x="26" y="45" text-anchor="middle" fill="var(--jewel-a)" font-size="7" font-family="monospace" letter-spacing="1">SIG</text>
+ <line x1="26" y1="26" x2="37" y2="13" stroke="var(--sevred)" stroke-width="2"/>
+ <circle cx="26" cy="26" r="3" fill="#1c1c1c" stroke="#000"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Volume</div>
+ <svg width="52" height="52" viewBox="0 0 52 52">
+ <circle cx="26" cy="26" r="24" fill="#0d0c0b" stroke="#90959a" stroke-width="2.4"/>
+ <g stroke="var(--jewel-a)" stroke-width="1.3" opacity=".9">
+ <line x1="26" y1="6" x2="26" y2="12"/><line x1="43" y1="13" x2="38.5" y2="17"/>
+ <line x1="46" y1="26" x2="40" y2="26"/><line x1="9" y1="13" x2="13.5" y2="17"/><line x1="6" y1="26" x2="12" y2="26"/>
+ </g>
+ <text x="26" y="45" text-anchor="middle" fill="var(--jewel-a)" font-size="7" font-family="monospace" letter-spacing="1">VOL</text>
+ <line x1="26" y1="26" x2="14" y2="14" stroke="var(--sevred)" stroke-width="2"/>
+ <circle cx="26" cy="26" r="3" fill="#1c1c1c" stroke="#000"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Tell-tales</div>
+ <div style="display:grid;grid-template-columns:auto auto;gap:5px 10px;background:#0d0c0b;border:2px solid #4b5055;border-radius:9px;padding:7px 10px;box-shadow:inset 0 2px 8px #000">
+ <span style="color:var(--jewel-g);font-size:11px;text-shadow:0 0 6px var(--jewel-g)">ᛒ BT</span>
+ <span style="color:var(--jewel-a);font-size:11px;text-shadow:0 0 6px var(--jewel-a)">⛭ SVC</span>
+ <span style="color:#3c3a36;font-size:11px">🔇 MUTE</span>
+ <span style="color:#3c3a36;font-size:11px">⚠ NET</span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Timer</div>
+ <div style="background:#0d0c0b;border:2px solid #4b5055;border-radius:9px;padding:6px 9px;color:var(--jewel-a);font-size:15px;text-shadow:0 0 7px var(--jewel-a);box-shadow:inset 0 2px 8px #000">12:34</div></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Tray</div>
+ <div style="display:flex;gap:6px;padding-top:9px"><i style="width:6px;height:6px;border-radius:50%;background:#44403a"></i><i style="width:6px;height:6px;border-radius:50%;background:#44403a"></i></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Weather</div>
+ <div style="border-radius:10px;padding:4px;background:var(--chrome-soft);box-shadow:0 3px 7px #000c">
+ <div style="background:#0d0c0b;border-radius:7px;padding:6px 11px;display:flex;align-items:center;gap:9px;color:var(--jewel-a);text-shadow:0 0 8px var(--jewel-a);box-shadow:inset 0 3px 9px #000">
+ <svg width="26" height="26" style="color:var(--jewel-a)"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="font-size:17px">82°</span>
+ <span style="font-size:8px;letter-spacing:.1em;color:#8a6f33;text-shadow:none">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#3a2f14;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="18" height="18" style="color:var(--jewel-a);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="font-size:11px;letter-spacing:.06em">WNW</span>
+ <span style="font-size:8px;color:#8a6f33;text-shadow:none">6 mph</span>
+ </div>
+ </div></div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Odometer date</div>
+ <div style="border-radius:10px;padding:4px;background:var(--chrome-soft);box-shadow:0 3px 7px #000c">
+ <div class="dgroup" style="background:#000;border-radius:7px;box-shadow:inset 0 3px 9px #000">
+ <div class="drum" style="font-size:16px">S</div><div class="drum" style="font-size:16px">A</div><div class="drum" style="font-size:16px">T</div>
+ <div class="drum" style="font-size:16px;background:linear-gradient(180deg,#eee,#bbb 50%,#999);color:#111">1</div>
+ <div class="drum" style="font-size:16px;background:linear-gradient(180deg,#eee,#bbb 50%,#999);color:#111">8</div>
+ <div class="drum" style="font-size:16px">J</div><div class="drum" style="font-size:16px">U</div><div class="drum" style="font-size:16px">L</div>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--jewel-a)">Clock · backlit</div>
+ <div style="border-radius:10px;padding:4px;background:var(--chrome-soft);box-shadow:0 3px 7px #000c">
+ <div style="background:#0d0c0b;border-radius:7px;padding:6px 11px;color:var(--jewel-a);font-size:19px;text-shadow:0 0 9px var(--jewel-a);box-shadow:inset 0 3px 9px #000">
+ 07:55 <span style="font-size:12px">PM</span> <span style="font-size:11px;color:#8a6f33">CDT</span>
+ </div></div></div>
+</div>
+<p class="cap"><b>The dashboard direction: amber-backlit gauge cluster with chrome top/bottom trim rails.</b> Net and volume are
+round needle gauges; bluetooth/maint/mute/net-fail live in a tell-tale block (lit = active, dark = off) exactly like a
+cluster's warning lamps; the date is a mixed odometer — white digits on the day drums (a real trip-counter trick); the clock
+glows amber behind chrome. The bar's top and bottom edges get the chrome rails, which is what makes the whole strip read
+"dashboard" at a glance. The amber family is the kit's existing panel amber, so this is the variation closest to the widget gallery.</p>
+</section>
+
+<!-- ================= V6 PERPETUAL CALENDAR ================= -->
+<section class="var">
+<h2><span class="n">6</span>Perpetual-calendar dress (guilloché + apertures)</h2>
+<div class="minibar" style="background:radial-gradient(circle at 50% 120%,#1d1a16,#0d0c0b 70%);border:1px solid #6e5a2e;box-shadow:inset 0 0 14px #000">
+ <span style="color:var(--steel)">▶ ✦ 1 <b style="color:var(--gold-hi)">2</b> 3 4 5 ▦ emacs — widgets.js</span><span class="sp"></span>
+ <span style="color:var(--steel)">AUX ᯤ ♪ ᛒ ⛭ ☾ ⋮⋮</span>
+ <span style="color:var(--cream);border:1px solid #6e5a2e;border-radius:3px;padding:1px 5px">SAT JUL · 18 · 7:55</span>
+</div>
+<div class="stage" style="background:repeating-radial-gradient(circle at 50% 260%,#161311 0 3px,#0f0d0b 3px 6px);border-radius:16px;border:1px solid #6e5a2e;box-shadow:inset 0 0 0 3px #060505, inset 0 1px 16px #000, 0 5px 18px #000">
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Aux</div><div class="knob brass" style="width:26px;height:26px"></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Net</div>
+ <div style="display:flex;gap:4px;padding-top:6px;align-items:center">
+ <span style="color:var(--cream);font-size:14px">ᯤ</span>
+ <span class="jewel" style="color:var(--gold-hi);background:radial-gradient(circle at 35% 30%,#ffe9b0,var(--gold) 60%,#7d5c16);width:8px;height:8px"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Volume</div>
+ <svg width="44" height="44" viewBox="0 0 44 44">
+ <circle cx="22" cy="22" r="20" fill="#0b0a09" stroke="#6e5a2e" stroke-width="1.4"/>
+ <circle cx="22" cy="22" r="14" fill="none" stroke="#3a301c" stroke-width=".8"/>
+ <line x1="22" y1="22" x2="31" y2="10" stroke="var(--gold-hi)" stroke-width="1.6"/>
+ <circle cx="22" cy="22" r="1.8" fill="var(--gold-hi)"/>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Blue</div>
+ <div style="display:flex;gap:4px;padding-top:6px;align-items:center">
+ <span style="color:var(--cream);font-size:14px">ᛒ</span>
+ <span class="jewel" style="color:var(--slate-hi);background:radial-gradient(circle at 35% 30%,#9db4cc,var(--slate-hi) 60%,#232c36);width:8px;height:8px"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Maint</div>
+ <div style="display:flex;gap:4px;padding-top:6px;align-items:center">
+ <span style="color:var(--cream);font-size:13px">⛭</span>
+ <span class="jewel" style="color:var(--jewel-a);background:radial-gradient(circle at 35% 30%,#ffd48a,var(--jewel-a) 60%,#6b4a12);width:8px;height:8px"></span>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Timer · moonphase</div>
+ <svg width="58" height="34" viewBox="0 0 58 34">
+ <defs><clipPath id="moonwin"><path d="M6 30 A24 24 0 0 1 52 30 Z"/></clipPath></defs>
+ <path d="M4 31 A26 26 0 0 1 54 31 Z" fill="#0b0a09" stroke="#6e5a2e" stroke-width="1.4"/>
+ <g clip-path="url(#moonwin)">
+ <rect x="0" y="0" width="58" height="34" fill="var(--slate)"/>
+ <circle cx="36" cy="18" r="9" fill="var(--cream)"/>
+ <circle cx="12" cy="10" r="1" fill="var(--cream)"/><circle cx="20" cy="22" r="1" fill="var(--cream)"/>
+ <circle cx="50" cy="9" r="1" fill="var(--cream)"/>
+ </g>
+ <text x="29" y="32.5" text-anchor="middle" fill="#8f7b48" font-size="6.5" font-family="monospace" letter-spacing="1">12:34</text>
+ </svg></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Tray</div>
+ <div style="display:flex;gap:6px;padding-top:9px"><i style="width:5px;height:5px;border-radius:50%;background:#4a3d22"></i><i style="width:5px;height:5px;border-radius:50%;background:#4a3d22"></i></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold)">Weather</div>
+ <div style="background:#0b0a09;border:1.6px solid var(--gold);border-radius:5px;padding:5px 11px;display:flex;align-items:center;gap:9px;box-shadow:inset 0 2px 8px #000">
+ <svg width="26" height="26" style="color:var(--gold-hi)"><use href="#wx-sun"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:var(--cream);font-size:16px">82°</span>
+ <span style="color:var(--gold);font-size:8px;letter-spacing:.1em">CLEAR</span>
+ </div>
+ <div style="width:1px;align-self:stretch;background:#4a3d22;margin:2px 0"></div>
+ <div style="display:flex;align-items:center;gap:5px">
+ <svg width="18" height="18" style="color:var(--gold-hi);transform:rotate(293deg);transform-origin:center"><use href="#wx-vane"/></svg>
+ <div style="display:flex;flex-direction:column;line-height:1.05">
+ <span style="color:var(--cream);font-size:11px;letter-spacing:.06em">WNW</span>
+ <span style="color:var(--gold);font-size:8px">6 mph</span>
+ </div>
+ </div></div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold-hi)">Day · Month apertures</div>
+ <div style="display:flex;gap:7px">
+ <div style="background:#0b0a09;border:1.6px solid var(--gold);border-radius:4px;padding:4px 9px;color:var(--cream);font-size:15px;letter-spacing:.1em;box-shadow:inset 0 2px 6px #000">SAT</div>
+ <div style="background:#0b0a09;border:1.6px solid var(--gold);border-radius:4px;padding:4px 9px;color:var(--cream);font-size:15px;letter-spacing:.1em;box-shadow:inset 0 2px 6px #000">JUL</div>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold-hi)">Big date</div>
+ <div style="display:flex;gap:3px">
+ <div style="background:var(--cream);color:#171310;border:1.6px solid var(--gold);border-radius:4px 0 0 4px;padding:3px 8px;font-size:20px;font-weight:700;box-shadow:inset 0 -2px 3px #0004">1</div>
+ <div style="background:var(--cream);color:#171310;border:1.6px solid var(--gold);border-radius:0 4px 4px 0;padding:3px 8px;font-size:20px;font-weight:700;box-shadow:inset 0 -2px 3px #0004">8</div>
+ </div></div>
+ <div class="mod"><div class="lbl" style="color:var(--gold-hi)">Time</div>
+ <div style="background:#0b0a09;border:1.6px solid var(--gold);border-radius:5px;padding:5px 11px;color:var(--cream);font-size:18px;letter-spacing:.06em;box-shadow:inset 0 2px 8px #000">
+ 7:55 <span style="font-size:11px;color:var(--gold)">PM CDT</span>
+ </div></div>
+</div>
+<p class="cap"><b>The Patek/AP direction: guilloché ground, gold-framed apertures, a moonphase.</b> Day and month are twin
+apertures (the SAM · NOV window pair), the date is a big-date double disc in cream, and the timer becomes the moonphase —
+the moon crosses the arc as the countdown runs, with the digits small beneath. Statuses are cabochon jewels, not lamps.
+The most jewelry-like and the most restrained: nothing glows except what matters. Least "stereo", most "watch".</p>
+</section>
+
+<div class="note">
+<b>Shared decisions across all six, for discussion:</b>
+(1) AUX knob at the head of the right cluster is the future desktop-settings module (caffeine + dim + touchpad fold in) —
+a knob in every variation since "settings" is the one module that IS a knob.
+(2) Left half stays functionally identical everywhere; it takes each variation's ground treatment only.
+(3) Date and time stay two separate modules (your current click/scroll contracts), even where they visually pair.
+(4) The drum content is the ordinal-free format — drums can't render "18th" gracefully; worth deciding whether the
+chronograph keeps ordinal at all.
+(5) True-proportion strips show the honest 3440×54 balance: the right cluster occupies roughly a quarter of the bar.
+(6) Weather module: the <b>compact chip is the chosen bar form</b> (2026-07-18) — it's the only one of the six weather takes legible at bar scale. The gauge, split-flap, VFD, nixie, and condition dial are kept as candidates for a larger panel or a click-to-expand view, not the bar.
+</div>
+
+</body>
+</html>
diff --git a/todo.org b/todo.org
index 1ac2e76..b4b1509 100644
--- a/todo.org
+++ b/todo.org
@@ -74,6 +74,8 @@ Date and time are separate fixed-position controls. The time display cycles its
own formats, including UTC; the date/calendar control cycles date-only formats
and never displays a second time. Implement the dedicated format rings,
tooltip behavior, and tests together in the dotfiles Waybar configuration.
+Reference material for the compact clock/chronograph treatment is filed in
+[[file:working/clock-display-references/][working/clock-display-references/]].
*** 2026-07-19 Sun @ 04:36:26 -0500 Folded clock-panel interaction direction
The clock-panel handoff settled the prior open question: UTC belongs only to
@@ -85,6 +87,54 @@ Reduce notification-sound playback loudness by 40% (0.6 gain, approximately
-4.4 dB). Change the =NOTIFY_VOLUME= playback control rather than re-encoding
the normalized sound files; verify each notification type still plays clearly.
+** TODO [#B] Assess a Hyprland left-drag window gesture :feature:hyprland:
+Evaluate whether a global left-click drag can move ordinary windows without
+breaking application selection, text interaction, or Wayland security
+expectations. Document the safe modifier/gesture alternatives before changing
+any binding.
+
+** TODO [#C] Add a time selector to the timer panel :feature:timer:
+Offer a period-appropriate selector for timer duration, likely drawing on the
+tape-counter idiom, while preserving the existing direct-entry path.
+
+** TODO [#C] Show the active wired interface in the Waybar network module :feature:waybar:network:
+When Ethernet is active, replace the offline-WiFi presentation with the wired
+interface glyph and interface name.
+
+** TODO [#C] Order network-panel connections by availability :feature:network:
+Present saved and currently available networks in this order: available saved
+profiles, available unsaved networks, then saved profiles that are unavailable.
+
+** TODO [#C] Let the clock panel dismiss itself on click :feature:clock:waybar:
+Make a left click inside the open clock panel toggle it closed; the Waybar time
+module remains the explicit way to reopen it.
+
+** TODO [#C] Indicate hotspot or metered WiFi in amber :feature:network:waybar:
+Detect hotspot/metered connectivity and render the WiFi icon plus SSID amber,
+while ordinary WiFi stays white.
+
+** TODO [#C] Make the WiFi toggle connect the best available profile :feature:network:
+When enabling WiFi, automatically connect to the highest-priority available
+saved network instead of requiring a panel selection first.
+
+** TODO [#B] Reconcile panel keybindings around Super+N :feature:hyprland:
+Swap the notification and networking bindings so primary panels are one
+Super-plus-letter chord away, audit the other exceptions, and bring the
+proposed family to Craig for a final mapping decision.
+
+** TODO [#B] Add storage-capacity signals to the maintenance module :feature:maint:
+Investigate capacity and growth diagnostics for full disks, identify the
+appropriate remedies, and incorporate a clear storage signal into the
+maintenance console.
+
+** TODO [#B] Add per-channel controls to the audio panel :feature:audio:
+Expose channel-level input and output volume controls without losing the
+existing device-level workflow.
+
+** TODO [#C] Add a whole-display dim mode :feature:hyprland:
+Extend auto-dim with an explicit “dim everything” setting for bright
+non-dark-mode contexts, with a security/usability review of its scope.
+
** DOING [#B] Widget gallery upgrades :feature:design:
:PROPERTIES:
:LAST_REVIEWED: 2026-07-13
@@ -254,6 +304,25 @@ The cross's cell assignments were read off card names and spec sheets, not audit
The panel widget gallery ([[file:docs/prototypes/panel-widget-gallery.html][docs/prototypes/panel-widget-gallery.html]]) grows into a retro-instrument component catalogue: reference photos of period hardware → gallery cards (the visual + behavioral spec) → reusable components for three targets (emacs svg.el, web/React, waybar). Tokens single-sourced in [[file:docs/prototypes/tokens.json][tokens.json]] (gen_tokens.py emits web/waybar/elisp); svg.el proof widget shipped (gallery-widget.el, needle gauge). Reference photos live in [[file:working/retro-stereo-widgets/][working/retro-stereo-widgets/]]. Collection converged at R56, then reopened at R57 as the taxonomy found empty cells (110 cards, all behaviorally verified; probes in [[file:tests/gallery-probes/][tests/gallery-probes/]]).
Build runs per the [[file:docs/specs/2026-07-12-component-generation-spec.org][component-generation spec]] (DOING; reviewed + decomposed 2026-07-12): web extraction first (ungated, lossless), then demand-gated Emacs/waybar ports. Banked variant/composition ledger lives in the 2026-07-11/12 session archive.
+*** TODO [#B] Rebuild the magic-eye tube component :feature:design:
+Reinstate the magic-eye tuning/level indicator (EM34/EM84/6E5 family), but
+replace the earlier weak UI rather than reviving it unchanged. The component
+must read as a real glass tube: P1-green phosphor bloom, a smooth shadow-angle
+response, and a clearly different tuning-minimum / record-level interaction.
+Build it to the current extraction-readiness bar with probes and a visual
+comparison before proposing ports. Reference: [[https://en.wikipedia.org/wiki/File:Em11-ani.gif][EM11 animation]].
+*** TODO [#B] Add a wind-direction rose component :feature:weather:design:
+Bank the weather companion from the Home handoff: a 16-point meteorological
+wind rose that reads source direction and optional speed, distinct from the
+aviation heading selector. Revisit when the weather surface needs more than
+the compact vane/speed slot; no implementation before that demand exists.
+*** 2026-07-19 Sun @ 15:40:01 -0500 Filed component and hardware references
+Inbox zero filed the clock/chronograph concepts in
+[[file:working/clock-display-references/][working/clock-display-references/]]
+and the knob, split-flap, and display references in the existing
+[[file:working/retro-stereo-widgets/references/][widget references]]. The Home
+component handoff was reconciled: Craig reinstated the magic eye subject to a
+better tube UI; the wind rose is banked for a future richer weather surface.
*** DOING [#B] Weather kit integration :feature:weather:dotfiles:
[[file:working/weather-kit-integration/][Working package]] received from Home on 2026-07-18. Land the shared, dependency-free =weather= CLI in dotfiles first, with cache/location/rendering tests; then wire a compact Dupre Waybar surface and port the supplied chip renderer to svg.el when its placement and interaction contract are settled. The chip itself remains current-conditions-only. Its hover exposes the next six hours; clicking it toggles a separate five-day forecast panel with each day's high/low and sunny/rainy condition. No coordinates ship in the repository: machines configure =$WEATHER_LAT=/$WEATHER_LON= or =~/.config/weather/config.json= (with =--geo= for travellers).
diff --git a/working/clock-display-references/2026-07-17-clock-strip-concept-01.png b/working/clock-display-references/2026-07-17-clock-strip-concept-01.png
new file mode 100644
index 0000000..6cd91c8
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-concept-01.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-concept-02.png b/working/clock-display-references/2026-07-17-clock-strip-concept-02.png
new file mode 100644
index 0000000..2edcebe
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-concept-02.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-concept-03.png b/working/clock-display-references/2026-07-17-clock-strip-concept-03.png
new file mode 100644
index 0000000..fb14c0c
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-concept-03.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-concept-04.png b/working/clock-display-references/2026-07-17-clock-strip-concept-04.png
new file mode 100644
index 0000000..d6bdd15
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-concept-04.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-concept-05.png b/working/clock-display-references/2026-07-17-clock-strip-concept-05.png
new file mode 100644
index 0000000..a2d3dda
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-concept-05.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-01.png b/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-01.png
new file mode 100644
index 0000000..6ae249a
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-01.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-02.png b/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-02.png
new file mode 100644
index 0000000..eb82fd1
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-02.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-03.png b/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-03.png
new file mode 100644
index 0000000..eb82fd1
--- /dev/null
+++ b/working/clock-display-references/2026-07-17-clock-strip-variant-sheet-03.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-audemars-moonphase.png b/working/clock-display-references/2026-07-18-chronograph-audemars-moonphase.png
new file mode 100644
index 0000000..bbf3404
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-audemars-moonphase.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-blancpain-fifty-fathoms.png b/working/clock-display-references/2026-07-18-chronograph-blancpain-fifty-fathoms.png
new file mode 100644
index 0000000..f4026c0
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-blancpain-fifty-fathoms.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-breitling-navitimer.png b/working/clock-display-references/2026-07-18-chronograph-breitling-navitimer.png
new file mode 100644
index 0000000..e518b2f
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-breitling-navitimer.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-date-idea-detail.png b/working/clock-display-references/2026-07-18-chronograph-date-idea-detail.png
new file mode 100644
index 0000000..7f3c8a9
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-date-idea-detail.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-date-idea.png b/working/clock-display-references/2026-07-18-chronograph-date-idea.png
new file mode 100644
index 0000000..8bc8a4b
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-date-idea.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-date-window-detail.png b/working/clock-display-references/2026-07-18-chronograph-date-window-detail.png
new file mode 100644
index 0000000..4ae7212
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-date-window-detail.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-iwc-portugieser.png b/working/clock-display-references/2026-07-18-chronograph-iwc-portugieser.png
new file mode 100644
index 0000000..d9d4616
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-iwc-portugieser.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-omega-speedmaster.png b/working/clock-display-references/2026-07-18-chronograph-omega-speedmaster.png
new file mode 100644
index 0000000..c62bf25
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-omega-speedmaster.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-panerai-luminor-marina.png b/working/clock-display-references/2026-07-18-chronograph-panerai-luminor-marina.png
new file mode 100644
index 0000000..b5a8d97
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-panerai-luminor-marina.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-patek-moonphase.png b/working/clock-display-references/2026-07-18-chronograph-patek-moonphase.png
new file mode 100644
index 0000000..a701931
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-patek-moonphase.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-patek-perpetual-calendar.png b/working/clock-display-references/2026-07-18-chronograph-patek-perpetual-calendar.png
new file mode 100644
index 0000000..11a5ac8
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-patek-perpetual-calendar.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-patek-two-register.png b/working/clock-display-references/2026-07-18-chronograph-patek-two-register.png
new file mode 100644
index 0000000..3cfa626
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-patek-two-register.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-reference-notes.txt b/working/clock-display-references/2026-07-18-chronograph-reference-notes.txt
new file mode 100644
index 0000000..b94cafe
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-reference-notes.txt
@@ -0,0 +1,20 @@
+Chronograph / watch-face design inspiration — 12 reference images.
+
+Collected by Craig and dropped from a home session on 2026-07-18 for the
+chronograph / clock-panel UI work. Each file is chronograph-inspiration-NN-<model>.png.
+
+Identified:
+01 Blancpain Fifty Fathoms
+02 Omega Speedmaster Professional (Moonwatch)
+03 Rolex Submariner Date
+05 Panerai Luminor Marina
+07 IWC Portugieser Chronograph
+08 Breitling Navitimer
+09 Patek Philippe perpetual calendar chronograph (resembles ref 1518)
+10 Audemars Piguet perpetual calendar moonphase
+11 Patek Philippe two-register chronograph (resembles ref 130)
+12 Patek Philippe perpetual calendar moonphase (3940/5140 family)
+
+Uncertain from the crop (relabel if the model is known):
+04 dial date-window detail — model unidentified
+06 Rolex fluted bezel + Cyclops date — Datejust or Day-Date, not certain
diff --git a/working/clock-display-references/2026-07-18-chronograph-rolex-cyclops-detail.png b/working/clock-display-references/2026-07-18-chronograph-rolex-cyclops-detail.png
new file mode 100644
index 0000000..1050032
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-rolex-cyclops-detail.png
Binary files differ
diff --git a/working/clock-display-references/2026-07-18-chronograph-rolex-submariner-date.png b/working/clock-display-references/2026-07-18-chronograph-rolex-submariner-date.png
new file mode 100644
index 0000000..e7bd485
--- /dev/null
+++ b/working/clock-display-references/2026-07-18-chronograph-rolex-submariner-date.png
Binary files differ
diff --git a/working/clock-display-references/README.org b/working/clock-display-references/README.org
new file mode 100644
index 0000000..f3b9bf2
--- /dev/null
+++ b/working/clock-display-references/README.org
@@ -0,0 +1,6 @@
+#+TITLE: Clock Display Reference Set
+
+Clock and chronograph reference material received during the 2026-07-19 inbox
+zero pass. The concept renders establish the compact date/time-strip direction;
+the watch photographs are reference-only for dial hierarchy, date treatment,
+and complication spacing. They are not assets to reproduce or redistribute.
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black-detail.jpeg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black-detail.jpeg
new file mode 100644
index 0000000..d723827
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black-detail.jpeg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black.jpg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black.jpg
new file mode 100644
index 0000000..8b2ed8e
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-black.jpg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender-detail.jpeg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender-detail.jpeg
new file mode 100644
index 0000000..6a07d1d
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender-detail.jpeg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender.webp b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender.webp
new file mode 100644
index 0000000..8223796
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-fender.webp
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-detail.jpeg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-detail.jpeg
new file mode 100644
index 0000000..2f2d72c
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-detail.jpeg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-pair.jpg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-pair.jpg
new file mode 100644
index 0000000..fecefdd
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-guitar-pair.jpg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-style-sheet.jpeg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-style-sheet.jpeg
new file mode 100644
index 0000000..3962c9f
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-style-sheet.jpeg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-tone-volume.jpg b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-tone-volume.jpg
new file mode 100644
index 0000000..e6e0cc0
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-dual-concentric-knob-tone-volume.jpg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-reference.org b/working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-reference.org
new file mode 100644
index 0000000..5fa7acd
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-reference.org
@@ -0,0 +1,65 @@
+#+TITLE: HotFX split-flap web component — technique reference
+#+DATE: [2026-07-18 Sat]
+
+Reference for the split-flap display work (gallery card N20, and any flip/drum
+date-time module the waybar chronograph grows). Craig supplied both links.
+
+- Annotated walkthrough: https://fx.hot.page/split-flap/source
+- Source on GitHub: https://github.com/hot-page/fx/blob/main/hotfx-split-flap/hotfx-split-flap.js
+- Local study copy: [[file:2026-07-18-hotfx-split-flap-source.js]]
+
+LICENSE CAUTION: the fx repo declares NO license (checked via the GitHub API
+2026-07-18), so the code is all-rights-reserved by default. Study the
+technique, re-derive our own implementation in the GW namespace — do not copy
+code or CSS verbatim into the gallery or dotfiles.
+
+* The mechanics worth stealing (as ideas)
+
+** Four-layer character cell
+Each character is four absolutely-stacked half-panels: =.top= / =.bottom=
+(current char) and =.top-next= / =.bottom-next= (incoming char), the tops
+clipped to the upper half and bottoms to the lower half, with a divider line
+at 50%. =transform-style: preserve-3d= on the cell,
+=backface-visibility: hidden= on flaps so a flap's reverse never shows.
+
+** One flip = two synchronized quarter-animations
+Only two elements move per flip, via the Web Animations API with
+=Promise.all= on their =.finished=:
+- current =.top= rotates =rotateX(0deg)= → =rotateX(-180deg)= (falls forward)
+- =.bottom-next= rotates =rotateX(180deg)= → =rotateX(0deg)= (lands)
+Both =ease-in=, default 150ms per flap. After landing, the static halves get
+the new character's text and the cycle repeats. This is the minimal honest
+split-flap kinematic.
+
+** The charset IS the drum
+It never jumps to the target character: it steps =currentIndex + 1= (wrapping)
+through the declared alphabet until it arrives, one flip per step — the
+Solari constraint that a board can only advance one flap at a time. The
+alphabet is a declared attribute (default =' ABC…Z0-9!.,:?"'/$'=), so the
+character order is data, exactly our R58 LAYOUT-as-data pattern: rewrite the
+alphabet and the physical behavior follows.
+
+** Live-update semantics (relevant for a clock)
+A MutationObserver rebuilds the target grid on text change; an in-flight flip
+cascade aborts itself by identity-checking the target grid each step, and the
+new animation starts only after awaiting every running animation's finish.
+That's the answer to "the minute changed while the drum was still spinning" —
+abort-and-retarget, never queue.
+
+** Also noted
+IntersectionObserver defers the animation until the element is half in view;
+=::part(flap-N-M)= attributes expose every flap and divider for external
+styling. ~300 lines total for all of it.
+
+* How it maps to our targets
+
+- Gallery N20 (split-flap board): the four-layer cell + two-animation flip is
+ the web implementation; the step-through-charset rule and abort-on-retarget
+ are behaviors our probe checks can pin regardless of implementation.
+- Waybar chronograph drums (the redesign mock's date/time): our drums are
+ odometer rollers, not flaps — different mechanism — but the charset-as-drum
+ ordering and abort-and-retarget rules carry over unchanged.
+- Emacs svg.el port: no Web Animations API; the flip becomes frame-stepped
+ whole-image regeneration per the render contract, with the same
+ one-step-at-a-time rule. Reduced-motion gate applies (widget-owned
+ animation per the gallery tick contract).
diff --git a/working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-source.js b/working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-source.js
new file mode 100644
index 0000000..40539ad
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-hotfx-split-flap-source.js
@@ -0,0 +1,455 @@
+// Create a custom element by extending `HTMLElement`.
+export class HotFXSplitFlap extends HTMLElement {
+ // The internal state of the component is handled in private instance
+ // variables, which in JavaScript become private when they start with `#`.
+ //
+ // `hasAnimated` is a flag that keeps track of whether the animation has
+ // already completed.
+ #hasAnimated = false
+ // `isVisible` is a flag that keeps track of whether the element is currently
+ // visible in the viewport.
+ #isVisible = false
+ // The `IntersectionObserver` fires a callback that lets us know if the element
+ // is visible in the window or not.
+ #intersectionObserver
+ // The `MutationObserver` tells the element when it's content changes, so we
+ // can re-render the grid and animate it again.
+ #mutationObserver
+ // The `#currentGrid` is the current state of the text. The characters are
+ // stored as a two-dimensional array: each row is an array of characters and
+ // the grid is an array of rows.
+ #currentGrid
+ // The `#targetGrid` shows the ultimate destination of the
+ #targetGrid
+
+ // Here we tell the browser to fire `attributeChangedCallback` when one of
+ // the `width`, `height`, or `duration` attributes changes.
+ static get observedAttributes() {
+ return ['width', 'height', 'duration', 'characcters']
+ }
+
+ constructor() {
+ // Always call your `super()` or the landlord will be mad.
+ super()
+ // Create a shadow DOM for this element that will hold all the elements and
+ // stlyes needed to create the flaps.
+ this.attachShadow({ mode: 'open' })
+ }
+
+ connectedCallback() {
+ // First do some validation on the attributes. Although all of them are
+ // optional, they are required to be positive integers if set.
+ ['width', 'height', 'duration'].forEach(name => {
+ if (!this.hasAttribute(name)) return
+ const value = parseInt(this.getAttribute(name))
+ if (isNaN(value) || value <= 0) this.#printNumberError(name)
+ })
+
+ // If this element has already been mounted elsewhere the `#hasAnimated`
+ // flag may be set even though we're coming in fresh here. Let's reset it.
+ this.#hasAnimated = false
+ // The `#render()` function will create the grid and the flaps inside the
+ // shadow DOM.
+ this.#render()
+
+ // Setup our `IntersectionObserver`, which will fire a callback function
+ // when this element scrolls into view.
+ this.#intersectionObserver = new IntersectionObserver(
+ (entries) => {
+ // Since we are only observing one element, the callback should always
+ // fire with one value in `entries`.
+ this.#isVisible = entries[0].isIntersecting
+ // The animation should only run the first time that we scoll onto it
+ // so only trigger the animation it if #hasAnimated is false.
+ if (this.#isVisible && !this.#hasAnimated) this.#animate()
+ },
+ // Fire the callback when 50% of this element has scrolled into view.
+ { threshold: 0.5 }
+ )
+ // Observe this element
+ this.#intersectionObserver.observe(this)
+
+ // Setup the `MutationObserver` that will re-run the animation when the
+ // text inside this element changes.
+ this.#mutationObserver = new MutationObserver(async () => {
+ // The `#targetGrid` is the string of text that the element will animate
+ // towards.
+ this.#updateTargetGrid()
+ // First wait for the current animations to finish so there's no glitch
+ // when the new animations start.
+ await Promise.all(
+ Array.from(this.shadowRoot.querySelectorAll('.char'))
+ .map(char => char.getAnimations())
+ .flat()
+ .map(animation => animation.finish())
+ )
+ // If the element is currently in view, start the animation.
+ if (this.#isVisible) this.#animate()
+ })
+ // Observe this element, along with the entire tree under it.
+ this.#mutationObserver.observe(
+ this,
+ { childList: true, characterData: true, subtree: true },
+ )
+ }
+
+ // Clean up the observers so that memory doesn't leak out of the browser,
+ // causing it to lose all of it's life force.
+ disconnectedCallback() {
+ this.#intersectionObserver?.disconnect()
+ this.#intersectionObserver = undefined
+ this.#mutationObserver?.disconnect()
+ this.#mutationObserver = undefined
+ }
+
+ // The browser will call this when one of the `observedAttributes` changes.
+ attributeChangedCallback(name, oldValue, newValue) {
+ // Do some validation of the new value to warn the page author if they've
+ // added an invalid value.
+ if (['width', 'height', 'duration'].includes(name)) {
+ const value = parseInt(this.getAttribute(name))
+ if (isNaN(value) || value <= 0) return this.#printNumberError(name)
+ }
+ // If the attribute that was changed is `duration` we don't neeed to do
+ // anything because it will be picked up in the next animation.
+ if (name == 'duration' || oldValue == newValue) return
+ // If `width` or `height` change, we will need to re-render the structure
+ // of the shadow DOM to account for the new size.
+ this.#render()
+ // Finally, restart the animation if this element is currently in the
+ // viewport. After rendering, the tiles are all on the first letter which is
+ // the space character by default.
+ if (this.#isVisible) this.#animate()
+ }
+
+ // Getter for `width` property called when reading `element.width`.
+ get width() {
+ // If there is a `#currentGrid`, report this size.
+ if (this.#currentGrid) return this.#currentGrid[0].length
+ // Otherwise, try to use the attribute if set.
+ if (this.hasAttribute('width')) {
+ const width = parseInt(this.getAttribute('width'))
+ if (Number.isInteger(width) && width > 0) return width
+ }
+ // Finally, if there is no grid and no attribute, we default to the size of
+ // the text inside this element, specifically the longest line.
+ return Math.max(...this.textContent.split('\n').map(line => line.length))
+ }
+
+ // Reflect the `.width` property back to the `width=` attribute.
+ set width(value) {
+ // Validate the new value which should be a positive integer.
+ if (!Number.isInteger(value) || value <= 0) this.#printNumberError('width')
+ // Reflect the new value to the attribute and re-render the shadow DOM
+ this.setAttribute('width', value)
+ this.#render()
+ // If the element is in the viewport, run the animation now.
+ if (this.#isVisible) this.#animate()
+ }
+
+ // Getter for `.height` property similar to above for `width`, but returning
+ // the number of lines in the text content instead of the longest line.
+ get height() {
+ if (this.#currentGrid) return this.#currentGrid.length
+ if (this.hasAttribute('height')) {
+ const height = parseInt(this.getAttribute('height'))
+ if (Number.isInteger(height) && height > 0) return height
+ }
+ return this.textContent.split('\n').length
+ }
+
+ // Setter for `.height` property, same as above for `width`.
+ set height(value) {
+ if (!Number.isInteger(value) || value <= 0) this.#printNumberError('height')
+ this.setAttribute('height', value)
+ this.#render()
+ if (this.#isVisible) this.#animate()
+ }
+
+ // Get the duration of the animation which is how long it takes for one flap
+ // to fall down from top to bottom.
+ get duration() {
+ // The duration defaults to 150 milliseconds or 0.15 seconds for each flap
+ // to fall once.
+ let duration = 150
+ // Check if there's a `duration` attribute and use that value if it's a
+ // positive integer.
+ if (this.hasAttribute('duration')) {
+ const durationAttr = parseInt(this.getAttribute('duration'))
+ if (Number.isInteger(durationAttr) && durationAttr > 0) duration = durationAttr
+ }
+ return duration
+ }
+
+ // Setter for duration that reflects to the attribute with valdiation logic.
+ set duration(value) {
+ if (typeof value !== 'number' || value <= 0) return this.#printNumberError('duration')
+ this.setAttribute('duration', value)
+ }
+
+ // Getter for `.characters` property. This is the alphabet used for the flaps.
+ // Here we're just pulling from the attribute which has the current value or
+ // using the default Latin alphabet.
+ get characters() {
+ return this.getAttribute('characters') || ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!.,:?"\'/$'
+ }
+
+ // Setter for `.characters` property that reflects to the attribute.
+ set characters(value) {
+ this.setAttribute('characters', value)
+ }
+
+ // Helper function that turns the text of this element into the grid of
+ // letters that we're going to animate towards. The grid is stored as a
+ // two-dimensional array of characters.
+ #updateTargetGrid() {
+ // Split the text on newline to get an array of lines.
+ const lines = this.textContent.split('\n')
+ // Note that we're using the `.width` and `.height` properties because the
+ // dimensions of the grid could have been set explicitly using attributes.
+ this.#targetGrid = Array.from({ length: this.height }).map((_, row) => {
+ const line = lines[row] || ''
+ return Array.from({ length: this.width }).map(
+ (_, col) => line[col]?.toUpperCase() || ' ',
+ )
+ })
+ }
+
+ // Render the grid of letters into the shadow DOM.
+ #render() {
+ // First set `#currentGrid` to null because it will be used to calculate the
+ // width and height of the next grid and we need to start fresh.
+ this.#currentGrid = null
+ // The `#currentGrid` is the current state of the letters displayed. Since
+ // we're starting over fresh here, we just make it all space characters.
+ this.#currentGrid = Array.from({ length: this.height }, () =>
+ Array.from({ length: this.width }, () => ' ')
+ )
+ this.#updateTargetGrid()
+
+ this.shadowRoot.innerHTML = [`
+ <style>
+ :host {
+ display: inline-block;
+ font-family: Arial, sans-serif;
+ }
+
+ #container {
+ display: grid;
+ grid-template-columns: repeat(${this.width}, auto);
+ grid-template-rows: repeat(${this.height}, auto);
+ gap: var(--hotfx-split-flap-grid-gap, .1em);
+ perspective: 1000px;
+ }`,
+ // Each character has height and width set explicitly which is not ideal
+ // but that way we have a grid of same-sized objects. If a given font
+ // has very wide characters, this can be overwritten using the
+ // `::part(char)` pseudo-element.
+ `.char {
+ position: relative;
+ width: 1em;
+ height: 1.2em;
+ transform-style: preserve-3d;
+ }`,
+ // These are the flaps that rotate to reveal each character. They are
+ // four elements positioned on top of each other, two for the current
+ // character and two for the next character -- each split into top
+ // and bottom halves.
+ `.top, .bottom, .top-next, .bottom-next {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ color: white;
+ background: black;`,
+ // backface-visibility is important to hide the back of the flap when
+ // it is rotated more than 90 degrees.
+ `backface-visibility: hidden;
+ }`,
+ // The top flaps have their bottoms cut off and the bottom flaps have
+ // their tops cut off.
+ `.top, .top-next {
+ clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
+ }
+
+ .bottom, .bottom-next {
+ clip-path: polygon(0 50%, 100% 50%, 100% 100%, 0 100%);
+ }`,
+ // The divider is the line in the middle of the flap.
+ `.divider {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background-color: currentColor;
+ color: #999;
+ z-index: 10;
+ transform: translateY(-50%);
+ }
+ </style>
+ <div id="container">`,
+ // Create the grid of characters by mapping over the `#currentGrid`
+ // (in both dimensions)
+ this.#currentGrid.map((row, rowNumber) => {
+ // Row and column numbers used in `::part()` start at 1 as per CSS
+ // convention, e.g. `nth-child(1)`
+ rowNumber += 1
+ return row.map((char, columnNumber) => {
+ columnNumber += 1
+ const currentChar = char === ' ' ? '&nbsp;' : char
+ // Each character and its flaps have several part= attribute values
+ // so they can be addressed in CSS, for example
+ // - `::part(char-1)` for all the characters in the first column
+ // - `::part(flap-3-4)` for the flaps in the third row and
+ // fourth column
+ return `
+ <span
+ class=char
+ part="char char-${columnNumber} char-${rowNumber}-${columnNumber}">
+ <span
+ class=top-next
+ part="flap flap-${columnNumber} flap-${rowNumber}-${columnNumber}"
+ aria-hidden=true>
+ ${currentChar}
+ <span
+ class=divider
+ part="divider flap-${columnNumber} divider-${rowNumber}-${columnNumber}">
+ </span>
+ </span>
+ <span
+ class=bottom-next
+ part="flap flap-${columnNumber} flap-${rowNumber}-${columnNumber}"
+ aria-hidden=true>
+ ${currentChar}
+ <span
+ class=divider
+ part="divider flap-${columnNumber} divider-${rowNumber}-${columnNumber}">
+ </span>
+ </span>
+ <span
+ class=top
+ part="flap flap-${columnNumber} flap-${rowNumber}-${columnNumber}">
+ ${currentChar}
+ <span
+ class=divider
+ part="divider flap-${columnNumber} divider-${rowNumber}-${columnNumber}">
+ </span>
+ </span>
+ <span
+ class=bottom
+ part="flap flap-${columnNumber} flap-${rowNumber}-${columnNumber}"
+ aria-hidden=true>
+ ${currentChar}
+ <span
+ class=divider
+ part="divider flap-${columnNumber} divider-${rowNumber}-${columnNumber}">
+ </span>
+ </span>
+ </span>
+ `
+ }).join('')
+ }).join(''),
+ `</div>`
+ ].join('')
+ }
+
+ // To animate, the element must rotate each flap, then swap out the characters
+ // in the DOM and then repeat through the whole alphabet until it arrives at
+ // the correct end character.
+ #animate() {
+ // If the animation has already run, don't run it again.
+ this.#hasAnimated = true
+
+ // Create a copy of the target grid so that we can check if it since been
+ // updated and the animation should be aborted (since another one will
+ // be triggered).
+ const targetGrid = this.#targetGrid
+
+ // Animate each character.
+ this.shadowRoot.querySelectorAll('.char').forEach((char, index) => {
+ // Find the index of this charcter in the grid.
+ const row = Math.floor(index / this.width)
+ const col = index % this.width
+
+ // The index of the target character in our alphabet.
+ const targetCharIndex = this.characters.includes(targetGrid[row][col])
+ ? this.characters.indexOf(targetGrid[row][col])
+ : 0
+
+ // The current character index in the alphabet.
+ let currentCharIndex = this.characters.indexOf(this.#currentGrid[row][col])
+
+ // If the indexes are the same, it's already finished and this character
+ // should not be animated at all
+ if (currentCharIndex == targetCharIndex) return
+
+ // This function will run one flap and wait for it to finsih before
+ // calling itself again to animate the next flap.
+ const flap = async () => {
+ // If the target grid has changed since we started animating, abort
+ // this animation since another one will be triggered.
+ if (targetGrid != this.#targetGrid) return
+
+ // Find the index of the character following the current one, wrapping
+ // around to the start if needed.
+ const nextCharIndex = currentCharIndex < this.characters.length - 1
+ ? currentCharIndex + 1
+ : 0
+
+ // The next character to animate towards
+ const nextChar = this.characters[nextCharIndex]
+
+ // Set the text content of the top and bottom flaps to the next character
+ char.querySelector('.top-next').childNodes[0].textContent = nextChar
+ char.querySelector('.bottom-next').childNodes[0].textContent = nextChar
+
+ // Anaimte the top flap and the bottom of the next flap to fall from the
+ // top. Using `await Promise.all()` to wait for both animations to
+ // finish.
+ const options = { duration: this.duration, easing: 'ease-in' }
+ await Promise.all([
+ char.querySelector('.top').animate([
+ { transform: 'rotateX(0deg)' },
+ { transform: 'rotateX(-180deg)' }
+ ], options).finished,
+ char.querySelector('.bottom-next').animate([
+ { transform: 'rotateX(180deg)' },
+ { transform: 'rotateX(0deg)' }
+ ], options).finished
+ ])
+
+ // Swap out the top and bottom flaps and set the `currentCharIndex` value
+ // so that the next character is now the currect character.
+ char.querySelector('.top').childNodes[0].textContent = nextChar
+ char.querySelector('.bottom').childNodes[0].textContent = nextChar
+ currentCharIndex = nextCharIndex
+ // Update the #currentGrid with the new character.
+ this.#currentGrid[row][col] = this.characters[currentCharIndex]
+ // If the new currrent character is still not the target character,
+ // continue animating by calling this `flap()` function again.
+ if (currentCharIndex !== targetCharIndex) flap()
+ }
+
+ // Trigger the first animation for this character.
+ flap()
+ })
+ }
+
+ // Helper function to print errors for invalid attributes.
+ #printNumberError(name) {
+ console.error(`<hotfx-split-flap> ${name} attribute must be a positive integer; got "${this.getAttribute(name)}"`)
+ }
+
+}
+
+// Checks for a `?define` query parameter in the URL that was used to load this
+// script. So, for instance, if you load the script with `<script type="module" src="https://cdn.jsdelivr.net/npm/@hot-page/hotfx-counter@0.0.0?define=false">`,
+// this variable will contain the text "false".
+const define = new URL(import.meta.url).searchParams.get("define")
+// By default we register the above class as a custom element so that we can
+// use it in HTML with the `<hotfx-counter>` tag. That is, unless called with
+// the query parameter to prevent this.
+if (define != 'false') customElements.define('hotfx-split-flap', HotFXSplitFlap)
diff --git a/working/retro-stereo-widgets/references/2026-07-18-single-knob-black.webp b/working/retro-stereo-widgets/references/2026-07-18-single-knob-black.webp
new file mode 100644
index 0000000..95c312f
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-single-knob-black.webp
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-single-knob-index-arrow.jpeg b/working/retro-stereo-widgets/references/2026-07-18-single-knob-index-arrow.jpeg
new file mode 100644
index 0000000..41acdd8
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-single-knob-index-arrow.jpeg
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-split-flap-header.webp b/working/retro-stereo-widgets/references/2026-07-18-split-flap-header.webp
new file mode 100644
index 0000000..b031946
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-split-flap-header.webp
Binary files differ
diff --git a/working/retro-stereo-widgets/references/2026-07-18-terminal-display.jpg b/working/retro-stereo-widgets/references/2026-07-18-terminal-display.jpg
new file mode 100644
index 0000000..7eff3fe
--- /dev/null
+++ b/working/retro-stereo-widgets/references/2026-07-18-terminal-display.jpg
Binary files differ
diff --git a/working/weather-kit-integration/README.org b/working/weather-kit-integration/README.org
new file mode 100644
index 0000000..d04899f
--- /dev/null
+++ b/working/weather-kit-integration/README.org
@@ -0,0 +1,119 @@
+#+TITLE: Weather kit — for the Dupre kit / waybar / Wayland UX
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-18
+
+Built to completion elsewhere and handed to archsetup, which owns the Dupre kit,
+waybar, and the Wayland UI/UX and will maintain it from here. This note is the
+integration guide: what's in the package, the data contract, and how each
+surface consumes it.
+
+* What's in the package
+
+| File | Purpose |
+|----------------------+-------------------------------------------------------------------------|
+| =weather= | The fetcher. Python 3, stdlib only. Hits Open-Meteo, normalises, caches. |
+| =weather-glyphs.svg= | The custom condition glyph set (9 symbols, currentColor). |
+| =weather-chip.html= | Reference renderer: the chosen chip, wired to the contract. Port target.|
+| =README.org= | This file. |
+
+* The design decisions it encodes (settled over the 2026-07-18 session)
+
+- *Source: Open-Meteo, =model=best_match=.* No API key, no signup, non-commercial
+ free tier (10k calls/day; a 15-min poll is ~96/day). =best_match= auto-selects
+ the most accurate regional model — NOAA blend in the US, ECMWF/ICON abroad — so
+ the same binary is right on ratio in New Orleans and on velox travelling. For a
+ bar chip there's no perceptible accuracy gap vs the NWS API, and NWS is US-only,
+ so Open-Meteo is the single source. (NWS stays a possible *additive* feature
+ later, only for official US severe-weather alerts.)
+- *One fetcher, one cache, thin renderers.* Every surface shells out to =weather=
+ and renders the JSON. Nobody re-implements the API call or the cache. A shared
+ cache means several widgets polling still hit the API at most once per TTL and
+ all show the identical reading.
+- *Windy is a condition, not a compass reading.* Below the threshold the chip
+ shows the direction vane + speed; at/above it, the gust glyph + speed. Same
+ width either way. Threshold default *15 mph* (Beaufort 4 — where wind stops
+ being ambient). It's a =--windy-threshold= flag, derived at output from
+ =wind_mph=, so changing it takes effect without a cache bust.
+- *Custom glyphs, not Nerd Font.* Seven conditions + a gust mark + a vane, drawn
+ to sit in the instrument aesthetic. =currentColor=, so each surface recolours.
+
+* The contract (=weather --json=)
+
+#+begin_src json
+{
+ "temp_f": 82, "feels_f": 92,
+ "condition": "clear", // clear | partly | cloud | fog | rain | snow | storm
+ "wmo_code": 0,
+ "windy": false, // wind_mph >= threshold
+ "wind_mph": 6, "wind_dir": "WNW", "wind_deg": 293,
+ "location": "New Orleans",
+ "observed": "2026-07-18T01:00", "stale": false
+}
+#+end_src
+
+=condition= is the coupling to the glyphs. Mapping (also in =weather-chip.html=):
+=clear->wx-sun=, =partly->wx-partly=, =cloud->wx-cloud=, =fog->wx-fog=,
+=rain->wx-rain=, =snow->wx-snow=, =storm->wx-storm=; plus =wx-wind= (shown when
+=windy=) and =wx-vane= (shown otherwise, rotated by =wind_deg=). A consumer never
+sees the WMO number unless it wants =wmo_code=.
+
+=stale: true= means the network fetch failed and this is the last good reading —
+render it dimmed, never blank the bar.
+
+* WMO weather-code -> condition (what the fetcher collapses)
+
+| WMO code(s) | condition |
+|--------------------------------------+-----------|
+| 0 | clear |
+| 1, 2 | partly |
+| 3 | cloud |
+| 45, 48 | fog |
+| 51-57, 61-67, 80-82 | rain |
+| 71-77, 85-86 | snow |
+| 95, 96, 99 | storm |
+| (anything unmapped) | cloud |
+
+* Install
+
+Drop =weather= on =PATH= the way the other fleet CLIs travel (=agent-page=, etc.):
+into the rulesets/dotfiles bin so =make install= / stow links it to
+=~/.local/bin/weather= on every machine. It's dependency-free, so nothing else to
+provision.
+
+No coordinates are baked in (nothing personal ships in the repo). Each machine
+sets its own location, resolved in order:
+
+1. =--lat/--lon= flags.
+2. =$WEATHER_LAT= / =$WEATHER_LON= (and optional =$WEATHER_NAME=).
+3. =~/.config/weather/config.json= — ={"lat":29.97,"lon":-90.09,"name":"New Orleans"}=.
+ Add ="use_geo": true= (or pass =--geo=) to prefer =whereami= — the right call
+ on velox, which travels.
+
+* How each surface consumes it
+
+- *waybar* — a custom module:
+ #+begin_src jsonc
+ "custom/weather": { "exec": "weather --format waybar", "return-type": "json",
+ "interval": 900, "on-click": "..." }
+ #+end_src
+ =--format waybar= emits ={text, alt, class, tooltip}=; the stylesheet picks the
+ icon by =alt=/=class= (=.clear=, =.windy=, =.stale=, ...).
+- *Dupre kit / =svg.el=* — =(json-parse-string (shell-command-to-string "weather --json"))=,
+ then render the chip. =weather-chip.html='s =renderChip(reading)= is the exact
+ logic to port: glyph, temp, wind slot (gust vs vane by =windy=).
+- *clock-panel (Python)* — =json.loads(subprocess.run(["weather","--json"],...).stdout)=.
+ No HTTP in clock-panel at all.
+
+* Notes / open choices for the maintainer
+
+- Poll cadence lives with the consumer (waybar =interval=), not the tool; the tool
+ just honours its own cache TTL (=--ttl=, default 900s).
+- =weather-glyphs.svg= is the canonical glyph source. =weather-chip.html= inlines a
+ copy for a standalone demo — if the glyphs change, update both (or have the kit
+ build inline from the =.svg=).
+- The chip currently shows the wind slot always. If it ever reads busy on a packed
+ bar, hiding the slot below the threshold (show only when windy) is a one-line
+ change — the data's already there.
+- A "windy later today" hint would want the hourly endpoint (=hourly=wind_speed_10m=)
+ and a small max-over-next-N-hours; the fetcher is structured to add that as a
+ second field without touching consumers.
diff --git a/working/weather-kit-integration/weather b/working/weather-kit-integration/weather
new file mode 100755
index 0000000..6d0c0f1
--- /dev/null
+++ b/working/weather-kit-integration/weather
@@ -0,0 +1,215 @@
+#!/usr/bin/env python3
+"""weather -- one fetcher for the whole fleet.
+
+Hits Open-Meteo (model=best_match, so NOAA blend in the US, ECMWF/ICON abroad),
+normalises the reading, caches the last good value, and prints it. Every app
+(waybar, clock-panel, an Emacs svg.el panel) shells out to this and renders the
+JSON -- nobody re-implements the API call, the code mapping, or the cache.
+
+Stdlib only: no pip, no venv. Runs as a bare executable.
+
+Contract (--format json):
+ {
+ "temp_f": 82, "feels_f": 92,
+ "condition": "clear", # clear|partly|cloud|fog|rain|snow|storm
+ "wmo_code": 0,
+ "windy": false, # wind_mph >= threshold (default 15)
+ "wind_mph": 6, "wind_dir": "WNW", "wind_deg": 293,
+ "location": "New Orleans",
+ "observed": "2026-07-18T01:00", "stale": false
+ }
+
+Location resolves in order: --lat/--lon, then $WEATHER_LAT/$WEATHER_LON, then
+~/.config/weather/config.json, then `whereami` (with --geo). No coordinates are
+baked in, so nothing personal ships in the repo -- each machine configures its
+own.
+"""
+import argparse
+import json
+import os
+import subprocess
+import sys
+import time
+import urllib.error
+import urllib.request
+from pathlib import Path
+
+API = "https://api.open-meteo.com/v1/forecast"
+CONFIG = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "weather" / "config.json"
+CACHE = Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache")) / "weather" / "current.json"
+DEFAULT_TTL = 900 # 15 min -- weather changes slowly; keep API calls rare
+DEFAULT_WINDY = 15 # mph; Beaufort 4, where wind stops being ambient
+
+# WMO weather-interpretation code -> our semantic condition.
+# Ranges collapse to the seven condition glyphs; "windy" is derived from speed.
+_WMO = {
+ 0: "clear",
+ 1: "partly", 2: "partly",
+ 3: "cloud",
+ 45: "fog", 48: "fog",
+ 51: "rain", 53: "rain", 55: "rain", 56: "rain", 57: "rain",
+ 61: "rain", 63: "rain", 65: "rain", 66: "rain", 67: "rain",
+ 80: "rain", 81: "rain", 82: "rain",
+ 71: "snow", 73: "snow", 75: "snow", 77: "snow", 85: "snow", 86: "snow",
+ 95: "storm", 96: "storm", 99: "storm",
+}
+
+_CARDINALS = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
+ "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"]
+
+
+def condition_for(code):
+ return _WMO.get(int(code), "cloud") # unknown code -> cloud, the safe neutral
+
+
+def cardinal(deg):
+ return _CARDINALS[int((deg % 360) / 22.5 + 0.5) % 16]
+
+
+def resolve_location(args):
+ """Return (lat, lon, name). Raise if nothing configured."""
+ if args.lat is not None and args.lon is not None:
+ return args.lat, args.lon, args.location
+ env_lat, env_lon = os.environ.get("WEATHER_LAT"), os.environ.get("WEATHER_LON")
+ if env_lat and env_lon:
+ return float(env_lat), float(env_lon), args.location or os.environ.get("WEATHER_NAME")
+ if CONFIG.exists():
+ cfg = json.loads(CONFIG.read_text())
+ if args.geo or cfg.get("use_geo"):
+ geo = _whereami()
+ if geo:
+ return geo[0], geo[1], args.location or cfg.get("name")
+ if "lat" in cfg and "lon" in cfg:
+ return cfg["lat"], cfg["lon"], args.location or cfg.get("name")
+ if args.geo:
+ geo = _whereami()
+ if geo:
+ return geo[0], geo[1], args.location
+ raise SystemExit(
+ "weather: no location. Pass --lat/--lon, set $WEATHER_LAT/$WEATHER_LON, "
+ f"write {CONFIG} (\"lat\"/\"lon\"[/\"name\"]), or use --geo on a machine "
+ "that travels."
+ )
+
+
+def _whereami():
+ """Best-effort coords from the `whereami` tool. Only meaningful where the
+ machine actually moves (velox); silently gives up otherwise."""
+ try:
+ out = subprocess.run(["whereami"], capture_output=True, text=True, timeout=20).stdout
+ except (OSError, subprocess.SubprocessError):
+ return None
+ for line in out.splitlines():
+ if "coords:" in line:
+ try:
+ nums = line.split("coords:")[1].split("(")[0]
+ lat, lon = (float(x) for x in nums.replace(",", " ").split()[:2])
+ return lat, lon
+ except (ValueError, IndexError):
+ return None
+ return None
+
+
+def fetch(lat, lon, name, threshold):
+ params = (
+ f"?latitude={lat}&longitude={lon}"
+ "&current=temperature_2m,apparent_temperature,weather_code,"
+ "wind_speed_10m,wind_direction_10m"
+ "&temperature_unit=fahrenheit&wind_speed_unit=mph"
+ "&models=best_match&timezone=auto"
+ )
+ req = urllib.request.Request(API + params, headers={"User-Agent": "weather-cli/1.0"})
+ with urllib.request.urlopen(req, timeout=15) as resp:
+ data = json.load(resp)
+ cur = data["current"]
+ mph = round(cur["wind_speed_10m"])
+ deg = cur["wind_direction_10m"]
+ return {
+ "temp_f": round(cur["temperature_2m"]),
+ "feels_f": round(cur["apparent_temperature"]),
+ "condition": condition_for(cur["weather_code"]),
+ "wmo_code": int(cur["weather_code"]),
+ "windy": mph >= threshold,
+ "wind_mph": mph,
+ "wind_dir": cardinal(deg),
+ "wind_deg": round(deg),
+ "location": name or f"{lat:.2f},{lon:.2f}",
+ "observed": cur["time"],
+ "stale": False,
+ }
+
+
+def read_cache():
+ try:
+ blob = json.loads(CACHE.read_text())
+ return blob["fetched_at"], blob["reading"]
+ except (OSError, ValueError, KeyError):
+ return None, None
+
+
+def write_cache(reading):
+ CACHE.parent.mkdir(parents=True, exist_ok=True)
+ CACHE.write_text(json.dumps({"fetched_at": int(time.time()), "reading": reading}))
+
+
+def get_reading(args):
+ """Cache-first, network-second, stale-cache-on-failure. One shared cache
+ means five widgets polling still hit the API at most once per TTL."""
+ fetched_at, cached = read_cache()
+ if cached and not args.no_cache and fetched_at and (time.time() - fetched_at) < args.ttl:
+ return cached
+ lat, lon, name = resolve_location(args)
+ try:
+ reading = fetch(lat, lon, name, args.windy_threshold)
+ write_cache(reading)
+ return reading
+ except (urllib.error.URLError, OSError, KeyError, ValueError) as e:
+ if cached: # never blank the bar: serve last good, flagged
+ cached["stale"] = True
+ return cached
+ raise SystemExit(f"weather: fetch failed and no cache to fall back on ({e})")
+
+
+def render(reading, fmt):
+ if fmt == "json":
+ return json.dumps(reading)
+ if fmt == "plain":
+ wind = "windy" if reading["windy"] else reading["wind_dir"]
+ s = f"{reading['temp_f']}°F {reading['condition']}, wind {wind} {reading['wind_mph']}"
+ return s + (" (stale)" if reading["stale"] else "")
+ if fmt == "waybar":
+ # Data only; the caller's stylesheet picks the icon by `alt`/`class`.
+ wind = "windy" if reading["windy"] else f"{reading['wind_dir']} {reading['wind_mph']}"
+ return json.dumps({
+ "text": f"{reading['temp_f']}° {reading['wind_mph']}",
+ "alt": reading["condition"],
+ "class": (["stale"] if reading["stale"] else []) + [reading["condition"]]
+ + (["windy"] if reading["windy"] else []),
+ "tooltip": f"{reading['location']}: {reading['temp_f']}°F "
+ f"(feels {reading['feels_f']}°), {reading['condition']}, "
+ f"wind {reading['wind_dir']} {reading['wind_mph']} mph"
+ + (" [stale]" if reading["stale"] else ""),
+ })
+ raise SystemExit(f"weather: unknown format {fmt!r}")
+
+
+def main(argv=None):
+ p = argparse.ArgumentParser(description="Normalised current weather from Open-Meteo.")
+ p.add_argument("--lat", type=float, help="latitude override")
+ p.add_argument("--lon", type=float, help="longitude override")
+ p.add_argument("--location", help="display name for the reading")
+ p.add_argument("--geo", action="store_true", help="geolocate via `whereami` (traveling machines)")
+ p.add_argument("--format", choices=["json", "plain", "waybar"], default="json")
+ p.add_argument("--windy-threshold", type=int, default=DEFAULT_WINDY, help="mph at which windy=true")
+ p.add_argument("--ttl", type=int, default=DEFAULT_TTL, help="cache seconds")
+ p.add_argument("--no-cache", action="store_true", help="force a fresh fetch")
+ args = p.parse_args(argv)
+ reading = get_reading(args)
+ # Derive windy from cached wind_mph so a changed threshold takes effect
+ # without waiting for the cache to expire.
+ reading["windy"] = reading["wind_mph"] >= args.windy_threshold
+ print(render(reading, args.format))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/working/weather-kit-integration/weather-chip.html b/working/weather-kit-integration/weather-chip.html
new file mode 100644
index 0000000..f9e05d4
--- /dev/null
+++ b/working/weather-kit-integration/weather-chip.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Weather chip — reference renderer</title>
+<style>
+:root{
+ --panel:#100f0f; --ground:#151311; --cream:#f3e7c5; --steel:#969385;
+ --gold:#e2a038; --gold-hi:#ffbe54; --amber-edge:#7d5c16;
+ --mono:"BerkeleyMono Nerd Font","Berkeley Mono",monospace;
+}
+*{box-sizing:border-box;margin:0;padding:0}
+body{background:var(--ground);color:var(--cream);font-family:var(--mono);padding:32px 4vw 80px}
+h1{color:var(--cream);font-size:19px;margin-bottom:6px}
+p{color:var(--steel);font-size:12.5px;max-width:820px;line-height:1.6;margin-bottom:8px}
+code{color:var(--gold-hi)}
+.row{display:flex;flex-wrap:wrap;gap:26px;align-items:flex-start;margin-top:26px}
+figure{margin:0;display:flex;flex-direction:column;align-items:center;gap:10px}
+figcaption{color:var(--steel);font-size:10px;letter-spacing:.14em;text-transform:uppercase}
+/* the chip */
+.wx-chip{display:inline-flex;align-items:center;gap:8px;background:#080808;border-radius:8px;
+ padding:6px 11px;box-shadow:inset 0 2px 8px #000, 0 0 0 1px #33290f}
+.wx-chip .temp{color:#e9e9e9;font-size:17px}
+.wx-chip .sep{width:1px;align-self:stretch;background:#33290f;margin:2px 0}
+.wx-chip .spd{color:var(--gold);font-size:13px}
+.wx-chip svg{display:block}
+</style>
+</head>
+<body>
+
+<h1>Weather chip — reference renderer</h1>
+<p>The chosen bar form, wired to the <code>weather</code> CLI contract. Each chip below is built by <code>renderChip(reading)</code> from a contract object (the JSON <code>weather --json</code> prints). This is the reference for the port into the Dupre kit's <code>svg.el</code>: condition glyph, temperature, then the wind slot — which collapses to the gust glyph + speed when <code>windy</code>, or the direction vane + speed when calm. Constant width either way.</p>
+<p>To drive it live: <code>weather --json | (feed into renderChip)</code>. The samples here are static so the condition and calm/windy mapping is visible at a glance.</p>
+
+<!-- the custom glyph set, inlined (same as weather-glyphs.svg) -->
+<svg width="0" height="0" style="position:absolute" aria-hidden="true"><defs>
+ <symbol id="wx-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4.3" fill="currentColor"/><g stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><line x1="12" y1="1.5" x2="12" y2="4.6"/><line x1="12" y1="19.4" x2="12" y2="22.5"/><line x1="1.5" y1="12" x2="4.6" y2="12"/><line x1="19.4" y1="12" x2="22.5" y2="12"/><line x1="4.6" y1="4.6" x2="6.8" y2="6.8"/><line x1="17.2" y1="17.2" x2="19.4" y2="19.4"/><line x1="4.6" y1="19.4" x2="6.8" y2="17.2"/><line x1="17.2" y1="6.8" x2="19.4" y2="4.6"/></g></symbol>
+ <symbol id="wx-partly" viewBox="0 0 24 24"><circle cx="8.5" cy="8" r="3.2" fill="currentColor"/><g stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="8.5" y1="1.5" x2="8.5" y2="3.4"/><line x1="1.7" y1="8" x2="3.6" y2="8"/><line x1="3.7" y1="3.2" x2="5" y2="4.5"/><line x1="13.3" y1="3.2" x2="12" y2="4.5"/></g><g fill="currentColor"><circle cx="12" cy="15" r="3.6"/><circle cx="16.5" cy="13" r="4.4"/><circle cx="20" cy="15.6" r="3.1"/><rect x="11" y="15" width="10" height="4" rx="2"/></g></symbol>
+ <symbol id="wx-cloud" viewBox="0 0 24 24"><g fill="currentColor"><circle cx="7.5" cy="13" r="4"/><circle cx="12.5" cy="10.5" r="5.2"/><circle cx="17" cy="13.5" r="3.6"/><rect x="6.5" y="13" width="11.5" height="4.2" rx="2.1"/></g></symbol>
+ <symbol id="wx-rain" viewBox="0 0 24 24"><g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g><g stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><line x1="8" y1="15.5" x2="6.6" y2="19.5"/><line x1="12" y1="15.5" x2="10.6" y2="19.5"/><line x1="16" y1="15.5" x2="14.6" y2="19.5"/></g></symbol>
+ <symbol id="wx-storm" viewBox="0 0 24 24"><g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g><path d="M12.5 14 L9 19.5 h3 L10.5 23.5 L15.5 17.5 h-3 L14 14 Z" fill="currentColor"/></symbol>
+ <symbol id="wx-snow" viewBox="0 0 24 24"><g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g><g stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><g transform="translate(9,17.5)"><line x1="-2" y1="0" x2="2" y2="0"/><line x1="0" y1="-2" x2="0" y2="2"/><line x1="-1.4" y1="-1.4" x2="1.4" y2="1.4"/><line x1="-1.4" y1="1.4" x2="1.4" y2="-1.4"/></g><g transform="translate(15,18.5)"><line x1="-2" y1="0" x2="2" y2="0"/><line x1="0" y1="-2" x2="0" y2="2"/><line x1="-1.4" y1="-1.4" x2="1.4" y2="1.4"/><line x1="-1.4" y1="1.4" x2="1.4" y2="-1.4"/></g></g></symbol>
+ <symbol id="wx-fog" viewBox="0 0 24 24"><g stroke="currentColor" stroke-width="1.9" stroke-linecap="round"><line x1="3" y1="7" x2="21" y2="7"/><line x1="5" y1="11.5" x2="19" y2="11.5"/><line x1="3" y1="16" x2="18" y2="16"/><line x1="7" y1="20.5" x2="21" y2="20.5"/></g></symbol>
+ <symbol id="wx-wind" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"><path d="M2.5 8 H13.5 a2.6 2.6 0 1 0 -2.55 -3.1"/><path d="M2.5 12.5 H18 a2.9 2.9 0 1 1 -2.85 3.45"/><path d="M2.5 17 H10.5 a2.3 2.3 0 1 1 -2.25 2.75"/></g></symbol>
+ <symbol id="wx-vane" viewBox="0 0 24 24"><path d="M12 2.4 L15.6 9 L12.9 9 L12.9 21 L11.1 21 L11.1 9 L8.4 9 Z" fill="currentColor" stroke="currentColor" stroke-width=".8" stroke-linejoin="round"/></symbol>
+</defs></svg>
+
+<div class="row" id="chips"></div>
+
+<script>
+// condition -> glyph id. Only "clear" differs from its name (historically the sun).
+const GLYPH = {clear:"wx-sun", partly:"wx-partly", cloud:"wx-cloud",
+ fog:"wx-fog", rain:"wx-rain", snow:"wx-snow", storm:"wx-storm"};
+
+// The whole render: contract object in, chip element out. This is the logic to
+// port to svg.el -- glyph, temp, then wind slot (gust glyph when windy, else the
+// vane rotated by wind_deg). Same footprint either way.
+function renderChip(r){
+ const chip = document.createElement("div");
+ chip.className = "wx-chip";
+ const cond = GLYPH[r.condition] || "wx-cloud";
+ chip.innerHTML =
+ `<svg width="24" height="24" style="color:var(--gold-hi)"><use href="#${cond}"/></svg>` +
+ `<span class="temp">${r.temp_f}°</span>` +
+ `<span class="sep"></span>` +
+ (r.windy
+ ? `<svg width="20" height="20" style="color:var(--gold-hi)"><use href="#wx-wind"/></svg>`
+ : `<svg width="18" height="18" style="color:var(--gold-hi);transform:rotate(${r.wind_deg}deg);transform-origin:center"><use href="#wx-vane"/></svg>`) +
+ `<span class="spd">${r.wind_mph}</span>`;
+ if (r.stale) chip.style.opacity = ".55";
+ return chip;
+}
+
+// Sample readings covering the condition set + the calm/windy collapse + stale.
+const SAMPLES = [
+ [{temp_f:94, condition:"clear", windy:false, wind_mph:9, wind_deg:335}, "clear · calm"],
+ [{temp_f:78, condition:"clear", windy:true, wind_mph:22, wind_deg:293}, "clear · windy"],
+ [{temp_f:61, condition:"rain", windy:false, wind_mph:7, wind_deg:120}, "rain"],
+ [{temp_f:58, condition:"storm", windy:true, wind_mph:31, wind_deg:200}, "storm · windy"],
+ [{temp_f:34, condition:"snow", windy:false, wind_mph:5, wind_deg:20}, "snow"],
+ [{temp_f:66, condition:"partly", windy:false, wind_mph:11, wind_deg:250}, "partly"],
+ [{temp_f:55, condition:"fog", windy:false, wind_mph:3, wind_deg:70}, "fog"],
+ [{temp_f:88, condition:"clear", windy:false, wind_mph:9, wind_deg:335, stale:true}, "stale (dimmed)"],
+];
+
+const host = document.getElementById("chips");
+for (const [reading, label] of SAMPLES){
+ const fig = document.createElement("figure");
+ fig.appendChild(renderChip(reading));
+ const cap = document.createElement("figcaption");
+ cap.textContent = label;
+ fig.appendChild(cap);
+ host.appendChild(fig);
+}
+</script>
+</body>
+</html>
diff --git a/working/weather-kit-integration/weather-glyphs.svg b/working/weather-kit-integration/weather-glyphs.svg
new file mode 100644
index 0000000..0cc3189
--- /dev/null
+++ b/working/weather-kit-integration/weather-glyphs.svg
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- weather-glyphs.svg — custom weather condition glyph set for the Dupre kit.
+ Drawn custom (not Nerd Font / emoji) so they sit in the instrument aesthetic.
+ Every symbol uses currentColor, so a consumer recolours by setting `color`
+ on the <svg>/<use> that references it. 24x24 viewBox throughout.
+
+ Condition ids match the `condition` field of the `weather` CLI:
+ clear -> wx-sun partly -> wx-partly cloud -> wx-cloud
+ fog -> wx-fog rain -> wx-rain snow -> wx-snow storm -> wx-storm
+ Plus two wind marks used by the chip's collapse rule:
+ wx-wind — the "windy" gust glyph (shown when wind_mph >= threshold)
+ wx-vane — the direction arrow (shown below threshold; rotate by wind_deg,
+ 0deg = points north, so the CSS transform is rotate(<wind_deg>deg))
+
+ Usage:
+ <svg width="24" height="24" style="color:#ffbe54"><use href="#wx-sun"/></svg>
+-->
+<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" style="position:absolute" aria-hidden="true"><defs>
+
+ <symbol id="wx-sun" viewBox="0 0 24 24">
+ <circle cx="12" cy="12" r="4.3" fill="currentColor"/>
+ <g stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
+ <line x1="12" y1="1.5" x2="12" y2="4.6"/><line x1="12" y1="19.4" x2="12" y2="22.5"/>
+ <line x1="1.5" y1="12" x2="4.6" y2="12"/><line x1="19.4" y1="12" x2="22.5" y2="12"/>
+ <line x1="4.6" y1="4.6" x2="6.8" y2="6.8"/><line x1="17.2" y1="17.2" x2="19.4" y2="19.4"/>
+ <line x1="4.6" y1="19.4" x2="6.8" y2="17.2"/><line x1="17.2" y1="6.8" x2="19.4" y2="4.6"/>
+ </g>
+ </symbol>
+
+ <symbol id="wx-partly" viewBox="0 0 24 24">
+ <circle cx="8.5" cy="8" r="3.2" fill="currentColor"/>
+ <g stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
+ <line x1="8.5" y1="1.5" x2="8.5" y2="3.4"/><line x1="1.7" y1="8" x2="3.6" y2="8"/>
+ <line x1="3.7" y1="3.2" x2="5" y2="4.5"/><line x1="13.3" y1="3.2" x2="12" y2="4.5"/>
+ </g>
+ <g fill="currentColor"><circle cx="12" cy="15" r="3.6"/><circle cx="16.5" cy="13" r="4.4"/><circle cx="20" cy="15.6" r="3.1"/><rect x="11" y="15" width="10" height="4" rx="2"/></g>
+ </symbol>
+
+ <symbol id="wx-cloud" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="13" r="4"/><circle cx="12.5" cy="10.5" r="5.2"/><circle cx="17" cy="13.5" r="3.6"/><rect x="6.5" y="13" width="11.5" height="4.2" rx="2.1"/></g>
+ </symbol>
+
+ <symbol id="wx-rain" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g>
+ <g stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><line x1="8" y1="15.5" x2="6.6" y2="19.5"/><line x1="12" y1="15.5" x2="10.6" y2="19.5"/><line x1="16" y1="15.5" x2="14.6" y2="19.5"/></g>
+ </symbol>
+
+ <symbol id="wx-storm" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g>
+ <path d="M12.5 14 L9 19.5 h3 L10.5 23.5 L15.5 17.5 h-3 L14 14 Z" fill="currentColor"/>
+ </symbol>
+
+ <symbol id="wx-snow" viewBox="0 0 24 24">
+ <g fill="currentColor"><circle cx="7.5" cy="9" r="3.6"/><circle cx="12.5" cy="7" r="4.6"/><circle cx="16.5" cy="9.6" r="3.1"/><rect x="6.5" y="9" width="10.5" height="3.8" rx="1.9"/></g>
+ <g stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
+ <g transform="translate(9,17.5)"><line x1="-2" y1="0" x2="2" y2="0"/><line x1="0" y1="-2" x2="0" y2="2"/><line x1="-1.4" y1="-1.4" x2="1.4" y2="1.4"/><line x1="-1.4" y1="1.4" x2="1.4" y2="-1.4"/></g>
+ <g transform="translate(15,18.5)"><line x1="-2" y1="0" x2="2" y2="0"/><line x1="0" y1="-2" x2="0" y2="2"/><line x1="-1.4" y1="-1.4" x2="1.4" y2="1.4"/><line x1="-1.4" y1="1.4" x2="1.4" y2="-1.4"/></g>
+ </g>
+ </symbol>
+
+ <symbol id="wx-fog" viewBox="0 0 24 24">
+ <g stroke="currentColor" stroke-width="1.9" stroke-linecap="round">
+ <line x1="3" y1="7" x2="21" y2="7"/><line x1="5" y1="11.5" x2="19" y2="11.5"/><line x1="3" y1="16" x2="18" y2="16"/><line x1="7" y1="20.5" x2="21" y2="20.5"/>
+ </g>
+ </symbol>
+
+ <!-- windy condition: flowing gust lines, each ending in a curl -->
+ <symbol id="wx-wind" viewBox="0 0 24 24">
+ <g fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round">
+ <path d="M2.5 8 H13.5 a2.6 2.6 0 1 0 -2.55 -3.1"/>
+ <path d="M2.5 12.5 H18 a2.9 2.9 0 1 1 -2.85 3.45"/>
+ <path d="M2.5 17 H10.5 a2.3 2.3 0 1 1 -2.25 2.75"/>
+ </g>
+ </symbol>
+
+ <!-- direction vane: points UP = north at 0deg; rotate by wind_deg (source bearing) -->
+ <symbol id="wx-vane" viewBox="0 0 24 24">
+ <path d="M12 2.4 L15.6 9 L12.9 9 L12.9 21 L11.1 21 L11.1 9 L8.4 9 Z" fill="currentColor" stroke="currentColor" stroke-width=".8" stroke-linejoin="round"/>
+ </symbol>
+
+</defs></svg>