[object Object]

← back to Gallery Agentabrams

add Grateful Dead collection to build gallery

797d116cb2d8d47c4152b46be3c62747945249b8 · 2026-09-01 13:18:15 -0700 · Steve Abrams

Files touched

Diff

commit 797d116cb2d8d47c4152b46be3c62747945249b8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 1 13:18:15 2026 -0700

    add Grateful Dead collection to build gallery
---
 README.md                                    |   5 +
 builds.json                                  |  55 ++
 builds/deadhead-print-lab/cover.png          | Bin 0 -> 574245 bytes
 builds/deadhead-print-lab/index.html         | 768 ++++++++++++++++++++++
 builds/long-strange-archive/cover.png        | Bin 0 -> 152145 bytes
 builds/long-strange-archive/index.html       | 289 +++++++++
 builds/objects-golden-road/cover.png         | Bin 0 -> 113587 bytes
 builds/objects-golden-road/index.html        | 328 ++++++++++
 builds/routes-residencies-rituals/cover.png  | Bin 0 -> 238461 bytes
 builds/routes-residencies-rituals/index.html | 924 +++++++++++++++++++++++++++
 builds/wall-of-sound/cover.png               | Bin 0 -> 163735 bytes
 builds/wall-of-sound/index.html              | 796 +++++++++++++++++++++++
 verification/grateful-dead-gallery.e2e.js    |  55 ++
 13 files changed, 3220 insertions(+)

diff --git a/README.md b/README.md
index 2e556f5..166b30a 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,11 @@ and **open-in-own-window** (the fleet hub UX).
 | № | Build | What |
 |---|-------|------|
 | 01 | Afterlight: Rooms Remember | Single-file digital exhibition — five objects (1928–2040), curator notes, provenance, conservation, audio transcripts, and Seedance motion studies. Compare mode, reading-progress, high-contrast, reduced-motion, keyboard-restorable reading position. |
+| 02 | The Long Strange Archive | Museum-grade Grateful Dead archival exhibition selected from Model Arena. |
+| 03 | Deadhead Print Lab | Interactive psychedelic print-studio and poster-process gallery. |
+| 04 | Wall of Sound: An Immersive Room | Virtual audio-architecture installation with multiple listening perspectives. |
+| 05 | Routes, Residencies & Rituals | Editorial tour-culture map and data story using clearly labeled illustrative data. |
+| 06 | Objects in the Golden Road | Decorative-arts catalog of imagined fan-culture artifacts and provenance. |
 
 ## Adding a build
 
diff --git a/builds.json b/builds.json
index 5c090e7..316dca1 100644
--- a/builds.json
+++ b/builds.json
@@ -11,6 +11,61 @@
       "cover_poster": "builds/afterlight/cover.jpg",
       "tags": ["Editorial", "Single-file", "Canvas", "Seedance", "A11y"],
       "added": "2026-09-01"
+    },
+    {
+      "num": 2,
+      "id": "long-strange-archive",
+      "file": "LongStrangeArchive.aa",
+      "title": "The Long Strange Archive",
+      "desc": "A museum-grade digital exhibition tracing the Grateful Dead’s visual and cultural journey through archival handbills, tickets, stage photography, fan-made objects, a venue map, and a warm interpretive timeline.",
+      "path": "builds/long-strange-archive/",
+      "cover_poster": "builds/long-strange-archive/cover.png",
+      "tags": ["Grateful Dead", "Archive", "Exhibition", "Qwen3", "A11y"],
+      "added": "2026-09-01"
+    },
+    {
+      "num": 3,
+      "id": "deadhead-print-lab",
+      "file": "DeadheadPrintLab.aa",
+      "title": "Deadhead Print Lab",
+      "desc": "An interactive print-studio gallery exploring psychedelic concert culture through original poster studies, color separations, registration marks, tactile overprint textures, process notes, and a full-screen lightbox.",
+      "path": "builds/deadhead-print-lab/",
+      "cover_poster": "builds/deadhead-print-lab/cover.png",
+      "tags": ["Grateful Dead", "Printmaking", "Posters", "Claude", "Interactive"],
+      "added": "2026-09-01"
+    },
+    {
+      "num": 4,
+      "id": "wall-of-sound",
+      "file": "WallOfSound.aa",
+      "title": "Wall of Sound: An Immersive Room",
+      "desc": "A virtual installation about live sound as architecture, with four listening perspectives, animated signal paths, an exploded technical view, equipment annotations, and an accessibility-minded quiet mode.",
+      "path": "builds/wall-of-sound/",
+      "cover_poster": "builds/wall-of-sound/cover.png",
+      "tags": ["Grateful Dead", "Installation", "Audio", "Claude", "Motion"],
+      "added": "2026-09-01"
+    },
+    {
+      "num": 5,
+      "id": "routes-residencies-rituals",
+      "file": "RoutesResidenciesRituals.aa",
+      "title": "Routes, Residencies & Rituals",
+      "desc": "A data-rich cultural-journalism gallery mapping fictionalized tour routes, venue residencies, seasonal movement, taping culture, and the human rituals surrounding a traveling musical community.",
+      "path": "builds/routes-residencies-rituals/",
+      "cover_poster": "builds/routes-residencies-rituals/cover.png",
+      "tags": ["Grateful Dead", "Data Story", "Cartography", "Claude", "Editorial"],
+      "added": "2026-09-01"
+    },
+    {
+      "num": 6,
+      "id": "objects-golden-road",
+      "file": "ObjectsGoldenRoad.aa",
+      "title": "Objects in the Golden Road",
+      "desc": "A collector-catalog exhibition presenting eight imagined fan-culture artifacts with accession records, materials, dimensions, provenance, condition notes, curator essays, comparison tools, and print mode.",
+      "path": "builds/objects-golden-road/",
+      "cover_poster": "builds/objects-golden-road/cover.png",
+      "tags": ["Grateful Dead", "Objects", "Catalog", "Qwen3", "Museum"],
+      "added": "2026-09-01"
     }
   ]
 }
diff --git a/builds/deadhead-print-lab/cover.png b/builds/deadhead-print-lab/cover.png
new file mode 100644
index 0000000..6965859
Binary files /dev/null and b/builds/deadhead-print-lab/cover.png differ
diff --git a/builds/deadhead-print-lab/index.html b/builds/deadhead-print-lab/index.html
new file mode 100644
index 0000000..577e213
--- /dev/null
+++ b/builds/deadhead-print-lab/index.html
@@ -0,0 +1,768 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Deadhead Print Lab — A Working Poster Studio</title>
+<style>
+  :root{
+    --cream:#f2e7cf;
+    --cream-fade:#e6d7b4;
+    --midnight:#16233f;
+    --midnight-deep:#0d1526;
+    --acid:#d9322f;
+    --marigold:#e8a63a;
+    --mint:#7ec9a8;
+    --ink:#1a1a20;
+    --paper-shadow:rgba(20,18,10,.28);
+    --sp1:4px;--sp2:8px;--sp3:12px;--sp4:16px;--sp5:24px;--sp6:32px;--sp7:48px;--sp8:64px;--sp9:96px;
+    --serif:'Didot','Bodoni MT','Playfair Display',Georgia,serif;
+    --sans:-apple-system,'Segoe UI','Helvetica Neue',Arial,sans-serif;
+    --radius:14px;
+    --card-shadow:0 8px 24px rgba(13,21,38,.22);
+    --hover-shadow:0 16px 48px rgba(13,21,38,.34);
+    --wall-cols:3;
+  }
+  *{box-sizing:border-box}
+  html{scroll-behavior:smooth}
+  body{
+    margin:0;
+    background:var(--midnight-deep);
+    color:var(--cream);
+    font-family:var(--sans);
+    font-size:16px;
+    line-height:1.55;
+    -webkit-font-smoothing:antialiased;
+    overflow-x:hidden;
+  }
+  /* Ambient drifting overprint field */
+  body::before{
+    content:"";
+    position:fixed;inset:0;z-index:0;pointer-events:none;
+    background:
+      radial-gradient(circle at 12% 18%, rgba(217,50,47,.14), transparent 42%),
+      radial-gradient(circle at 88% 22%, rgba(232,166,58,.13), transparent 40%),
+      radial-gradient(circle at 70% 82%, rgba(126,201,168,.12), transparent 44%),
+      radial-gradient(circle at 24% 88%, rgba(22,35,63,.5), transparent 50%);
+    background-size:180% 180%;
+    animation:drift 22s ease-in-out infinite alternate;
+    mix-blend-mode:screen;
+    opacity:.9;
+  }
+  @keyframes drift{
+    0%{background-position:0% 0%}
+    100%{background-position:100% 100%}
+  }
+  /* paper grain */
+  body::after{
+    content:"";position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.06;
+    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
+  }
+  .wrap{position:relative;z-index:1}
+
+  ::selection{background:var(--acid);color:var(--cream)}
+  a{color:var(--marigold)}
+  .visually-hidden{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
+
+  .allcaps{text-transform:uppercase;letter-spacing:.15em;font-size:12px;color:var(--marigold);font-weight:600}
+
+  /* ============ HEADER / MASTHEAD ============ */
+  header.masthead{
+    position:relative;
+    padding:var(--sp8) var(--sp5) var(--sp7);
+    text-align:center;
+    border-bottom:2px solid rgba(232,166,58,.35);
+    overflow:hidden;
+  }
+  .kicker{
+    display:inline-block;
+    border:1px solid var(--mint);
+    color:var(--mint);
+    padding:6px 14px;
+    border-radius:40px;
+    font-size:11px;letter-spacing:.22em;text-transform:uppercase;font-weight:700;
+    margin-bottom:var(--sp5);
+    animation:fadeUp .7s cubic-bezier(.22,1,.36,1) both;
+  }
+  h1.logo{
+    font-family:var(--serif);
+    font-weight:400;
+    font-size:clamp(42px,9vw,86px);
+    line-height:.94;
+    margin:0 0 var(--sp4);
+    letter-spacing:-.01em;
+    position:relative;
+  }
+  /* misregistered overprint title */
+  h1.logo .a{color:var(--cream)}
+  h1.logo .b{
+    display:block;
+    color:var(--acid);
+    position:relative;
+  }
+  h1.logo .b::after{
+    content:"Print Lab";
+    position:absolute;left:3px;top:2px;
+    color:var(--mint);
+    mix-blend-mode:screen;
+    opacity:.75;
+    animation:jitter 6s ease-in-out infinite;
+  }
+  @keyframes jitter{
+    0%,100%{transform:translate(0,0)}
+    25%{transform:translate(-2px,1px)}
+    50%{transform:translate(2px,-1px)}
+    75%{transform:translate(-1px,-1px)}
+  }
+  header.masthead p.lede{
+    max-width:640px;margin:0 auto;
+    font-size:clamp(16px,2.2vw,21px);
+    color:var(--cream-fade);
+    animation:fadeUp .7s cubic-bezier(.22,1,.36,1) .12s both;
+  }
+  .imprint{
+    margin-top:var(--sp6);
+    display:flex;gap:var(--sp5);justify-content:center;flex-wrap:wrap;
+    font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted,#9a8f74);
+    animation:fadeUp .7s cubic-bezier(.22,1,.36,1) .24s both;
+  }
+  .imprint span{color:#a99a78}
+  .imprint b{color:var(--marigold);font-weight:600}
+
+  @keyframes fadeUp{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}
+
+  /* ============ CONTROL BAR ============ */
+  .controls{
+    position:sticky;top:0;z-index:40;
+    background:rgba(13,21,38,.9);
+    backdrop-filter:blur(10px);
+    border-bottom:1px solid rgba(232,166,58,.25);
+    padding:var(--sp3) var(--sp5);
+    display:flex;flex-wrap:wrap;gap:var(--sp5);align-items:center;justify-content:center;
+  }
+  .ctl-group{display:flex;align-items:center;gap:var(--sp3);flex-wrap:wrap;justify-content:center}
+  .ctl-label{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--mint);font-weight:700}
+  .seps{display:flex;gap:6px;flex-wrap:wrap}
+  button.sep{
+    font-family:var(--sans);
+    font-size:12px;letter-spacing:.08em;text-transform:uppercase;font-weight:600;
+    padding:7px 12px;
+    border-radius:30px;
+    border:1px solid rgba(242,231,207,.3);
+    background:transparent;color:var(--cream);
+    cursor:pointer;transition:all .18s ease;
+  }
+  button.sep[data-sep="all"].active{background:var(--cream);color:var(--midnight-deep);border-color:var(--cream)}
+  button.sep[data-sep="red"].active{background:var(--acid);color:var(--cream);border-color:var(--acid)}
+  button.sep[data-sep="gold"].active{background:var(--marigold);color:var(--midnight-deep);border-color:var(--marigold)}
+  button.sep[data-sep="mint"].active{background:var(--mint);color:var(--midnight-deep);border-color:var(--mint)}
+  button.sep[data-sep="blue"].active{background:var(--midnight);color:var(--cream);border-color:var(--marigold)}
+  button.sep:hover{transform:translateY(-2px)}
+  button.sep:focus-visible,button.toggle:focus-visible,button.wall-card:focus-visible,.chip:focus-visible{outline:2px solid var(--marigold);outline-offset:3px}
+
+  button.toggle{
+    font-family:var(--sans);font-size:12px;letter-spacing:.08em;text-transform:uppercase;font-weight:600;
+    padding:7px 14px;border-radius:30px;cursor:pointer;transition:all .18s ease;
+    border:1px solid var(--marigold);background:transparent;color:var(--marigold);
+    display:inline-flex;align-items:center;gap:8px;
+  }
+  button.toggle[aria-pressed="true"]{background:var(--marigold);color:var(--midnight-deep)}
+  button.toggle:hover{transform:translateY(-2px)}
+
+  input[type=range]{
+    -webkit-appearance:none;appearance:none;width:120px;height:4px;border-radius:4px;
+    background:linear-gradient(90deg,var(--acid),var(--marigold),var(--mint));outline:none;cursor:pointer;
+  }
+  input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:18px;height:18px;border-radius:50%;background:var(--cream);border:2px solid var(--midnight-deep);cursor:pointer}
+  input[type=range]::-moz-range-thumb{width:18px;height:18px;border-radius:50%;background:var(--cream);border:2px solid var(--midnight-deep);cursor:pointer}
+
+  /* ============ POSTER WALL ============ */
+  main{padding:var(--sp8) var(--sp5) var(--sp9);max-width:1400px;margin:0 auto}
+  .section-head{text-align:center;margin-bottom:var(--sp7)}
+  .section-head h2{font-family:var(--serif);font-weight:400;font-size:clamp(28px,4vw,38px);margin:0 0 var(--sp2)}
+  .section-head p{color:var(--cream-fade);max-width:560px;margin:0 auto}
+
+  .wall{
+    display:grid;
+    grid-template-columns:repeat(var(--wall-cols),1fr);
+    gap:var(--sp6);
+    transition:gap .3s ease;
+  }
+  @media(max-width:900px){.wall{--wall-cols:2!important}}
+  @media(max-width:560px){.wall{--wall-cols:1!important}}
+
+  .poster-card{
+    background:transparent;border:none;padding:0;cursor:pointer;text-align:left;
+    animation:fadeUp .7s cubic-bezier(.22,1,.36,1) both;
+    display:flex;flex-direction:column;gap:var(--sp3);
+  }
+  .poster-frame{
+    position:relative;
+    border-radius:4px;
+    overflow:hidden;
+    box-shadow:var(--card-shadow);
+    transition:transform .25s cubic-bezier(.22,1,.36,1),box-shadow .25s ease;
+    /* torn / imperfect edge via clip */
+    background:var(--cream);
+  }
+  .poster-card:hover .poster-frame,.poster-card:focus-visible .poster-frame{transform:translateY(-6px) rotate(-.4deg);box-shadow:var(--hover-shadow)}
+  .poster-frame svg{display:block;width:100%;height:auto}
+  /* imperfect deckle edge overlay */
+  .poster-frame::after{
+    content:"";position:absolute;inset:0;pointer-events:none;
+    box-shadow:inset 0 0 0 6px var(--cream), inset 0 0 22px rgba(20,18,10,.22);
+    mix-blend-mode:multiply;opacity:.5;
+  }
+  .poster-meta{padding:0 2px}
+  .poster-meta .title{font-family:var(--serif);font-size:22px;line-height:1.1;margin:0 0 4px;color:var(--cream)}
+  .poster-meta .venue{font-size:13px;color:var(--marigold);letter-spacing:.04em}
+  .poster-meta .edition{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:#9a8f74;margin-top:4px}
+
+  /* Separation view: dim channels not selected */
+  svg .sep-red,svg .sep-gold,svg .sep-mint,svg .sep-blue,svg .reg{transition:opacity .35s ease}
+  .wall[data-view="red"] .sep-gold,.wall[data-view="red"] .sep-mint,.wall[data-view="red"] .sep-blue{opacity:.06}
+  .wall[data-view="gold"] .sep-red,.wall[data-view="gold"] .sep-mint,.wall[data-view="gold"] .sep-blue{opacity:.06}
+  .wall[data-view="mint"] .sep-red,.wall[data-view="mint"] .sep-gold,.wall[data-view="mint"] .sep-blue{opacity:.06}
+  .wall[data-view="blue"] .sep-red,.wall[data-view="blue"] .sep-gold,.wall[data-view="blue"] .sep-mint{opacity:.06}
+  svg .reg{opacity:0}
+  .wall.show-reg svg .reg{opacity:.9}
+
+  /* animated screen-print pull */
+  .poster-card.printing .ink-layer{animation:pull 1.1s cubic-bezier(.5,0,.2,1) both}
+  @keyframes pull{
+    from{transform:translateY(-14%);opacity:0}
+    to{transform:translateY(0);opacity:1}
+  }
+
+  /* ============ TYPE COMPARISON ============ */
+  .type-lab{margin-top:var(--sp9);padding-top:var(--sp7);border-top:2px solid rgba(232,166,58,.28)}
+  .type-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:var(--sp5);margin-top:var(--sp6)}
+  .type-card{
+    background:var(--cream);color:var(--midnight-deep);
+    border-radius:var(--radius);padding:var(--sp6) var(--sp5);
+    box-shadow:var(--card-shadow);position:relative;overflow:hidden;
+    transition:transform .2s ease,box-shadow .2s ease;
+  }
+  .type-card:hover{transform:translateY(-4px);box-shadow:var(--hover-shadow)}
+  .type-card::before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 80% 10%,rgba(217,50,47,.12),transparent 60%);pointer-events:none}
+  .type-card .tc-label{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--acid);font-weight:700;margin-bottom:var(--sp3)}
+  .type-card .specimen{font-size:clamp(30px,4.5vw,46px);line-height:1;margin:0 0 var(--sp3)}
+  .type-card .desc{font-size:14px;color:#4a4234;line-height:1.5}
+  .ts-didone .specimen{font-family:var(--serif);font-weight:400;letter-spacing:-.01em}
+  .ts-condensed .specimen{font-family:var(--sans);font-weight:800;letter-spacing:-.03em;transform:scaleX(.82);transform-origin:left}
+  .ts-woodblock .specimen{font-family:var(--serif);font-weight:700;letter-spacing:.02em;text-shadow:2px 2px 0 var(--marigold),4px 4px 0 rgba(217,50,47,.35)}
+  .ts-script .specimen{font-family:'Snell Roundhand','Segoe Script',cursive;font-style:italic;font-weight:400}
+
+  /* ============ PROCESS DRAWER ============ */
+  .drawer-tab{
+    position:fixed;right:0;top:50%;transform:translateY(-50%) rotate(0deg);z-index:55;
+    transform-origin:center;
+  }
+  .drawer-tab button{
+    writing-mode:vertical-rl;
+    background:var(--acid);color:var(--cream);border:none;
+    padding:var(--sp5) var(--sp2);font-size:12px;letter-spacing:.2em;text-transform:uppercase;font-weight:700;
+    border-radius:12px 0 0 12px;cursor:pointer;box-shadow:-6px 0 20px rgba(0,0,0,.35);
+    transition:background .2s ease;
+  }
+  .drawer-tab button:hover{background:#c22a27}
+  .drawer{
+    position:fixed;top:0;right:0;height:100%;width:min(440px,92vw);z-index:60;
+    background:var(--midnight);
+    border-left:3px solid var(--marigold);
+    box-shadow:-24px 0 60px rgba(0,0,0,.55);
+    transform:translateX(100%);transition:transform .4s cubic-bezier(.22,1,.36,1);
+    overflow-y:auto;padding:var(--sp7) var(--sp5) var(--sp8);
+  }
+  .drawer.open{transform:translateX(0)}
+  .drawer h3{font-family:var(--serif);font-weight:400;font-size:32px;margin:0 0 var(--sp2)}
+  .drawer .sub{color:var(--mint);font-size:12px;letter-spacing:.16em;text-transform:uppercase;margin-bottom:var(--sp6)}
+  .drawer .close{position:absolute;top:var(--sp4);right:var(--sp4);background:transparent;border:1px solid var(--cream);color:var(--cream);border-radius:50%;width:38px;height:38px;font-size:20px;cursor:pointer;line-height:1}
+  .drawer .close:hover{background:var(--acid);border-color:var(--acid)}
+  .proc-item{border-top:1px solid rgba(232,166,58,.28);padding:var(--sp5) 0}
+  .proc-item h4{margin:0 0 var(--sp2);font-size:15px;letter-spacing:.1em;text-transform:uppercase;color:var(--marigold);display:flex;align-items:center;gap:10px}
+  .proc-item .swatch{width:14px;height:14px;border-radius:3px;display:inline-block}
+  .proc-item p{margin:0;font-size:14px;color:var(--cream-fade);line-height:1.6}
+
+  /* ============ LIGHTBOX ============ */
+  .lightbox{
+    position:fixed;inset:0;z-index:80;display:none;
+    background:rgba(6,10,18,.94);
+    backdrop-filter:blur(6px);
+    padding:var(--sp5);
+    overflow-y:auto;
+  }
+  .lightbox.open{display:flex;align-items:flex-start;justify-content:center;animation:lbfade .3s ease both}
+  @keyframes lbfade{from{opacity:0}to{opacity:1}}
+  .lb-inner{
+    display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:var(--sp7);
+    max-width:1100px;width:100%;margin:auto;padding:var(--sp6) 0;
+  }
+  @media(max-width:820px){.lb-inner{grid-template-columns:1fr;gap:var(--sp5)}}
+  .lb-poster{background:var(--cream);border-radius:4px;overflow:hidden;box-shadow:0 24px 70px rgba(0,0,0,.6);position:relative}
+  .lb-poster svg{display:block;width:100%;height:auto}
+  .lb-info h3{font-family:var(--serif);font-weight:400;font-size:clamp(30px,5vw,46px);margin:0 0 var(--sp2);line-height:1}
+  .lb-info .lb-venue{color:var(--marigold);font-size:16px;margin-bottom:var(--sp5)}
+  .lb-curator{border-left:3px solid var(--acid);padding-left:var(--sp4);margin:var(--sp5) 0;color:var(--cream-fade);font-style:italic;line-height:1.7}
+  .lb-facts{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp3) var(--sp5);margin-top:var(--sp5)}
+  .lb-facts div{border-top:1px solid rgba(232,166,58,.28);padding-top:var(--sp2)}
+  .lb-facts .k{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mint)}
+  .lb-facts .v{font-size:15px;color:var(--cream)}
+  .lb-seps{display:flex;gap:8px;margin-top:var(--sp5);flex-wrap:wrap}
+  .lb-close{position:fixed;top:var(--sp4);right:var(--sp4);z-index:90;background:var(--acid);color:var(--cream);border:none;border-radius:50%;width:46px;height:46px;font-size:24px;cursor:pointer;line-height:1;box-shadow:0 6px 20px rgba(0,0,0,.4)}
+  .lb-close:hover{transform:rotate(90deg);transition:transform .3s ease}
+
+  /* ============ FOOTER ============ */
+  footer{
+    text-align:center;padding:var(--sp8) var(--sp5);
+    border-top:2px solid rgba(232,166,58,.28);
+    color:#9a8f74;font-size:13px;line-height:1.8;
+  }
+  footer .colophon{max-width:560px;margin:0 auto var(--sp5)}
+  footer .palette-row{display:flex;gap:6px;justify-content:center;margin-bottom:var(--sp5)}
+  footer .pd{width:34px;height:34px;border-radius:6px;border:1px solid rgba(242,231,207,.2)}
+
+  @media(prefers-reduced-motion:reduce){
+    *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
+    body::before{animation:none}
+  }
+</style>
+</head>
+<body>
+<div class="wrap">
+
+  <header class="masthead">
+    <span class="kicker">Museum of Vernacular Print · Study Wing</span>
+    <h1 class="logo"><span class="a">Deadhead</span><span class="b">Print Lab</span></h1>
+    <p class="lede">A working screen-print studio, opened for public view — five original poster studies drawn in the handmade visual language of concert culture, presented layer by layer.</p>
+    <div class="imprint">
+      <span>Curated Edition · <b>Vol. 03</b></span>
+      <span>Five Studies · <b>1971–1978 (imagined)</b></span>
+      <span>Ink on <b>sun-faded cream stock</b></span>
+    </div>
+  </header>
+
+  <div class="controls" role="region" aria-label="Studio controls">
+    <div class="ctl-group">
+      <span class="ctl-label" id="seplabel">Color separation</span>
+      <div class="seps" role="group" aria-labelledby="seplabel">
+        <button class="sep active" data-sep="all" aria-pressed="true">Full pull</button>
+        <button class="sep" data-sep="red" aria-pressed="false">Acid red</button>
+        <button class="sep" data-sep="gold" aria-pressed="false">Marigold</button>
+        <button class="sep" data-sep="mint" aria-pressed="false">Mint</button>
+        <button class="sep" data-sep="blue" aria-pressed="false">Midnight</button>
+      </div>
+    </div>
+    <div class="ctl-group">
+      <button class="toggle" id="regToggle" aria-pressed="false">✛ Registration marks</button>
+      <button class="toggle" id="repullBtn" aria-pressed="false">↻ Re-pull screens</button>
+    </div>
+    <div class="ctl-group">
+      <span class="ctl-label" id="denslabel">Wall density</span>
+      <input type="range" id="density" min="2" max="5" value="3" step="1" aria-labelledby="denslabel" aria-valuetext="3 columns">
+    </div>
+  </div>
+
+  <main>
+    <div class="section-head">
+      <h2>The Poster Wall</h2>
+      <p>Each study is a stack of hand-cut screens. Switch separations to isolate a single ink; reveal the registration crosses the printers used to line the screens up.</p>
+    </div>
+
+    <div class="wall" id="wall" data-view="all" aria-label="Poster studies"></div>
+
+    <section class="type-lab" aria-labelledby="typehead">
+      <div class="section-head">
+        <h2 id="typehead">Type Treatments, Compared</h2>
+        <p>The same three words set four ways — the tension between a refined didone and a battered woodblock is where the whole aesthetic lives.</p>
+      </div>
+      <div class="type-grid">
+        <div class="type-card ts-didone">
+          <div class="tc-label">Study A · High Didone</div>
+          <p class="specimen">Dance the Night</p>
+          <p class="desc">Extreme thick-to-thin contrast, tight tracking. Elegant, museum-poster restraint against the chaos of the imagery.</p>
+        </div>
+        <div class="type-card ts-woodblock">
+          <div class="tc-label">Study B · Woodblock Slab</div>
+          <p class="specimen">Dance the Night</p>
+          <p class="desc">Heavy serif knocked out of register on purpose — two colors kiss and shadow. Reads as vintage letterpress at ten paces.</p>
+        </div>
+        <div class="type-card ts-condensed">
+          <div class="tc-label">Study C · Condensed Gothic</div>
+          <p class="specimen">Dance the Night</p>
+          <p class="desc">Squeezed sans, maximum words per inch. The utilitarian voice of the handbill and the road case stencil.</p>
+        </div>
+        <div class="type-card ts-script">
+          <div class="tc-label">Study D · Loose Script</div>
+          <p class="specimen">Dance the Night</p>
+          <p class="desc">A ballpoint-loose hand — the human, off-the-cuff mark that keeps the whole composition from feeling designed.</p>
+        </div>
+      </div>
+    </section>
+  </main>
+
+  <!-- PROCESS DRAWER -->
+  <div class="drawer-tab">
+    <button id="drawerOpen" aria-expanded="false" aria-controls="drawer">Process notes</button>
+  </div>
+  <aside class="drawer" id="drawer" role="dialog" aria-modal="false" aria-labelledby="drawerTitle" aria-hidden="true">
+    <button class="close" id="drawerClose" aria-label="Close process notes">×</button>
+    <h3 id="drawerTitle">In the Shop</h3>
+    <p class="sub">How these studies were made</p>
+
+    <div class="proc-item">
+      <h4><span class="swatch" style="background:var(--acid)"></span>Ink & Overprint</h4>
+      <p>Four oil-based rubber inks pulled in strict order — midnight base, marigold, mint, then acid red as the top hit. Where two transparent inks overlap, a third color is <em>born</em> on the paper. Nothing is mixed in the bucket that could be mixed on the sheet.</p>
+    </div>
+    <div class="proc-item">
+      <h4><span class="swatch" style="background:var(--cream-fade)"></span>Paper & Deckle</h4>
+      <p>110 lb cover, cream, deliberately foxed and sun-faded before printing so no two sheets share a ground tone. Torn — never cut — to a natural deckle edge. The imperfection is the signature.</p>
+    </div>
+    <div class="proc-item">
+      <h4><span class="swatch" style="background:var(--marigold)"></span>Halftone</h4>
+      <p>Photographic passages are broken to a coarse 25-line elliptical dot, shot on lith film. Big enough to see with the naked eye — the dot is a texture, not a hidden trick.</p>
+    </div>
+    <div class="proc-item">
+      <h4><span class="swatch" style="background:var(--mint)"></span>Letterpress Hit</h4>
+      <p>Display type is set in salvaged wood type and run last on a Vandercook, biting a slight deboss into the screen ink so the words sit physically <em>below</em> the surface. Register is intentionally a hair loose.</p>
+    </div>
+    <div class="proc-item">
+      <h4><span class="swatch" style="background:var(--midnight)"></span>Registration</h4>
+      <p>Every screen is lined up to hand-drawn corner crosses (✛). Toggle them on across the wall to see the printer's scaffolding — the marks that would be trimmed off the final edition.</p>
+    </div>
+  </aside>
+
+  <!-- LIGHTBOX -->
+  <div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-labelledby="lbTitle">
+    <button class="lb-close" id="lbClose" aria-label="Close poster view">×</button>
+    <div class="lb-inner">
+      <div class="lb-poster" id="lbPoster"></div>
+      <div class="lb-info">
+        <h3 id="lbTitle"></h3>
+        <div class="lb-venue" id="lbVenue"></div>
+        <blockquote class="lb-curator" id="lbCurator"></blockquote>
+        <div class="lb-facts">
+          <div><div class="k">Edition</div><div class="v" id="lbEdition"></div></div>
+          <div><div class="k">Date</div><div class="v" id="lbDate"></div></div>
+          <div><div class="k">Screens</div><div class="v" id="lbScreens"></div></div>
+          <div><div class="k">Motif</div><div class="v" id="lbMotif"></div></div>
+        </div>
+        <div class="lb-seps" role="group" aria-label="Isolate a separation in this view">
+          <button class="sep active" data-lb="all" aria-pressed="true">Full</button>
+          <button class="sep" data-lb="red" aria-pressed="false">Red</button>
+          <button class="sep" data-lb="gold" aria-pressed="false">Gold</button>
+          <button class="sep" data-lb="mint" aria-pressed="false">Mint</button>
+          <button class="sep" data-lb="blue" aria-pressed="false">Blue</button>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <footer>
+    <div class="palette-row" aria-hidden="true">
+      <div class="pd" style="background:#e6d7b4" title="Sun-faded cream"></div>
+      <div class="pd" style="background:#16233f" title="Midnight blue"></div>
+      <div class="pd" style="background:#d9322f" title="Acid red"></div>
+      <div class="pd" style="background:#e8a63a" title="Marigold"></div>
+      <div class="pd" style="background:#7ec9a8" title="Mint"></div>
+    </div>
+    <p class="colophon">Deadhead Print Lab is a fictional studio and museum exhibit. All motifs — dancing skeletons, roses, lightning, celestial landscapes, road cases — are newly drawn abstractions, not reproductions of any existing logo or artwork. Venues, dates, and edition numbers are imagined for the study.</p>
+    <p class="allcaps">A self-contained artifact · No external resources</p>
+  </footer>
+
+</div>
+
+<script>
+(function(){
+  "use strict";
+
+  /* ---------- POSTER DATA + PROCEDURAL SVG ---------- */
+  // Each poster is a set of layered SVG groups tagged by separation channel.
+  // Motifs are abstract shapes: skeleton, rose, lightning, celestial, road case.
+
+  const REG = `
+    <g class="reg" stroke="#16233f" stroke-width="1.2" opacity="0.9">
+      <g transform="translate(22,22)"><line x1="-9" y1="0" x2="9" y2="0"/><line x1="0" y1="-9" x2="0" y2="9"/><circle r="7" fill="none"/></g>
+      <g transform="translate(378,22)"><line x1="-9" y1="0" x2="9" y2="0"/><line x1="0" y1="-9" x2="0" y2="9"/><circle r="7" fill="none"/></g>
+      <g transform="translate(22,538)"><line x1="-9" y1="0" x2="9" y2="0"/><line x1="0" y1="-9" x2="0" y2="9"/><circle r="7" fill="none"/></g>
+      <g transform="translate(378,538)"><line x1="-9" y1="0" x2="9" y2="0"/><line x1="0" y1="-9" x2="0" y2="9"/><circle r="7" fill="none"/></g>
+    </g>`;
+
+  // halftone dot pattern def (coarse)
+  function defs(id){
+    return `<defs>
+      <pattern id="ht${id}" width="9" height="9" patternUnits="userSpaceOnUse" patternTransform="rotate(15)">
+        <rect width="9" height="9" fill="none"/>
+        <circle cx="4.5" cy="4.5" r="2.4" fill="currentColor"/>
+      </pattern>
+      <filter id="rough${id}"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" result="n"/><feDisplacementMap in="SourceGraphic" in2="n" scale="2.4"/></filter>
+    </defs>`;
+  }
+
+  // --- Motif drawers (return svg strings placed in a group) ---
+  function skeletonDance(){ // dancing skeleton abstraction
+    return `
+      <g class="ink-layer sep-blue" fill="none" stroke="#16233f" stroke-width="7" stroke-linecap="round" filter="url(#rough1)">
+        <circle cx="200" cy="150" r="34"/>
+        <path d="M200 184 L200 300"/>
+        <path d="M200 210 L150 260 M200 210 L258 250"/>
+        <path d="M200 300 L160 400 M200 300 L246 405"/>
+      </g>
+      <g class="ink-layer sep-red" fill="#d9322f" filter="url(#rough1)">
+        <circle cx="188" cy="145" r="6"/><circle cx="214" cy="145" r="6"/>
+        <path d="M175 300 q25 22 50 0 l4 20 q-29 22 -58 0 Z"/>
+      </g>
+      <g class="ink-layer sep-gold" fill="none" stroke="#e8a63a" stroke-width="4" stroke-linecap="round">
+        <path d="M150 260 l-14 -8 -14 10 M258 250 l16 -6 12 12"/>
+      </g>`;
+  }
+  function roseBloom(){
+    return `
+      <g class="ink-layer sep-red" fill="#d9322f" filter="url(#rough2)">
+        <path d="M200 250 q-70 -60 0 -120 q70 60 0 120 Z"/>
+        <path d="M200 250 q-90 -20 -60 -110 q80 30 60 110 Z"/>
+        <path d="M200 250 q90 -20 60 -110 q-80 30 -60 110 Z"/>
+        <circle cx="200" cy="215" r="26" fill="#16233f"/>
+      </g>
+      <g class="ink-layer sep-mint" fill="none" stroke="#7ec9a8" stroke-width="9" stroke-linecap="round">
+        <path d="M200 270 L200 470"/>
+        <path d="M200 360 q-70 -20 -95 -70 M200 400 q70 -10 100 -55"/>
+      </g>
+      <g class="ink-layer sep-gold" fill="#e8a63a">
+        <circle cx="200" cy="215" r="9"/>
+      </g>`;
+  }
+  function lightningStorm(){
+    return `
+      <g class="ink-layer sep-blue" fill="#16233f">
+        <path d="M40 40 h320 v480 h-320 Z" opacity="0.16"/>
+      </g>
+      <g class="ink-layer sep-gold" fill="#e8a63a" filter="url(#rough3)">
+        <path d="M230 60 L150 280 L210 280 L120 500 L250 250 L190 250 Z"/>
+      </g>
+      <g class="ink-layer sep-red" fill="none" stroke="#d9322f" stroke-width="6">
+        <path d="M60 120 q80 40 0 80 M300 380 q-70 40 0 80"/>
+      </g>
+      <g class="ink-layer sep-mint" fill="#7ec9a8" opacity="0.9">
+        <circle cx="90" cy="90" r="5"/><circle cx="320" cy="150" r="5"/><circle cx="300" cy="470" r="5"/><circle cx="80" cy="440" r="5"/>
+      </g>`;
+  }
+  function celestial(){
+    return `
+      <g class="ink-layer sep-blue" fill="#16233f">
+        <path d="M40 300 q160 -120 320 0 v220 h-320 Z"/>
+      </g>
+      <g class="ink-layer sep-gold" fill="#e8a63a" filter="url(#rough4)">
+        <circle cx="200" cy="180" r="70"/>
+      </g>
+      <g class="ink-layer sep-red" fill="#d9322f">
+        <path d="M200 110 a70 70 0 0 0 0 140 a52 52 0 0 1 0 -140 Z"/>
+      </g>
+      <g class="ink-layer sep-mint" fill="#7ec9a8">
+        <path d="M90 90 l6 14 15 2 -11 11 3 15 -13 -8 -13 8 3 -15 -11 -11 15 -2 Z"/>
+        <path d="M320 260 l4 10 11 1 -8 8 2 11 -9 -6 -9 6 2 -11 -8 -8 11 -1 Z"/>
+        <circle cx="300" cy="120" r="4"/><circle cx="120" cy="230" r="4"/>
+      </g>
+      <g class="ink-layer sep-mint" fill="none" stroke="#7ec9a8" stroke-width="3" opacity="0.7">
+        <path d="M40 360 q160 -110 320 0"/>
+      </g>`;
+  }
+  function roadCase(){
+    return `
+      <g class="ink-layer sep-blue" fill="#16233f" filter="url(#rough5)">
+        <rect x="90" y="180" width="220" height="220" rx="8"/>
+      </g>
+      <g class="ink-layer sep-gold" fill="none" stroke="#e8a63a" stroke-width="8">
+        <rect x="90" y="180" width="220" height="220" rx="8"/>
+        <path d="M90 240 h220 M90 340 h220"/>
+      </g>
+      <g class="ink-layer sep-red" fill="#d9322f">
+        <circle cx="120" cy="210" r="9"/><circle cx="280" cy="210" r="9"/>
+        <circle cx="120" cy="370" r="9"/><circle cx="280" cy="370" r="9"/>
+        <rect x="180" y="285" width="40" height="30" rx="4"/>
+      </g>
+      <g class="ink-layer sep-mint" fill="#7ec9a8">
+        <path d="M150 470 q50 -30 100 0" stroke="#7ec9a8" stroke-width="6" fill="none"/>
+        <circle cx="130" cy="470" r="14"/><circle cx="270" cy="470" r="14"/>
+      </g>`;
+  }
+
+  const MOTIFS = [skeletonDance, roseBloom, lightningStorm, celestial, roadCase];
+
+  const POSTERS = [
+    { id:1, title:"Skeleton Waltz", venue:"The Marigold Ballroom, Eugene OR",
+      edition:"AP 12 / 90", date:"May 3, 1972", screens:"4 screens · 1 letterpress hit", motif:"Dancing skeleton",
+      curator:"Study 01 isolates the figure to almost nothing — a skull, a spine, four thrown limbs. The joy is entirely in the gesture, printed loose so the red mouth and blue bones never quite line up. That slippage is the dance.",
+      draw:skeletonDance, htColor:"#16233f" },
+    { id:2, title:"Rose in Overprint", venue:"Cascade Field House, Bozeman MT",
+      edition:"38 / 120", date:"Sept 19, 1974", screens:"4 screens · coarse halftone", motif:"Rose",
+      curator:"A single bloom where the acid red petals cross a midnight core to make a bruised violet the bucket never held. The mint stem is printed last and low, so it debosses into the ground — you can feel it with a fingertip.",
+      draw:roseBloom, htColor:"#d9322f" },
+    { id:3, title:"Thirteen-Point Bolt", venue:"Old Armory, Winslow AZ",
+      edition:"07 / 75", date:"July 11, 1976", screens:"4 screens · knocked-out gold", motif:"Lightning",
+      curator:"The bolt is knocked out of a marigold field and split down the middle by a hairline of register drift. Two mint sparks and a red charge in the corners keep the eye ricocheting around the sheet.",
+      draw:lightningStorm, htColor:"#e8a63a" },
+    { id:4, title:"Celestial Landscape", venue:"Lantern Amphitheater, Taos NM",
+      edition:"51 / 150", date:"Oct 2, 1977", screens:"5 screens · split-fountain sky", motif:"Celestial",
+      curator:"A sun eclipsed to a crescent over rolling midnight hills, with mint stars hand-placed off the grid. The split-fountain blend was pulled once and never repeated, so this is the only true state.",
+      draw:celestial, htColor:"#e8a63a" },
+    { id:5, title:"Road Case No. 9", venue:"The Freight Depot, Council Bluffs IA",
+      edition:"22 / 60", date:"Feb 28, 1978", screens:"4 screens · stencil face", motif:"Road case",
+      curator:"The most utilitarian study — a flight case rendered as pure stencil, gold rule over blue body, red latches. It's a portrait of the labor behind the show: the thing that carries the whole rig to the next town.",
+      draw:roadCase, htColor:"#16233f" }
+  ];
+
+  function posterSVG(p, extra){
+    const idx = p.id;
+    return `<svg viewBox="0 0 400 560" role="img" aria-label="${p.title} — ${p.motif} poster study" preserveAspectRatio="xMidYMid meet">
+      ${defs(idx)}
+      <rect x="0" y="0" width="400" height="560" fill="#e6d7b4"/>
+      <!-- faded overprint texture -->
+      <g color="#16233f" opacity="0.10"><rect x="30" y="430" width="340" height="110" fill="url(#ht${idx})"/></g>
+      ${p.draw()}
+      <!-- title bar -->
+      <g class="ink-layer sep-blue">
+        <rect x="30" y="452" width="340" height="70" fill="#16233f"/>
+      </g>
+      <text x="200" y="497" text-anchor="middle" font-family="Didot,'Bodoni MT',Georgia,serif" font-size="30" fill="#f2e7cf" class="sep-red">${p.title}</text>
+      <text x="200" y="540" text-anchor="middle" font-family="Helvetica,Arial,sans-serif" font-size="11" letter-spacing="2" fill="#e8a63a" class="sep-gold">${p.date.toUpperCase()}</text>
+      ${REG}
+    </svg>`;
+  }
+
+  /* ---------- RENDER WALL ---------- */
+  const wall = document.getElementById('wall');
+  POSTERS.forEach(function(p,i){
+    const card = document.createElement('button');
+    card.className = 'poster-card';
+    card.style.animationDelay = (i*90)+'ms';
+    card.setAttribute('aria-label','Open '+p.title+' in full view');
+    card.innerHTML =
+      '<div class="poster-frame">'+posterSVG(p)+'</div>'+
+      '<div class="poster-meta">'+
+        '<p class="title">'+p.title+'</p>'+
+        '<p class="venue">'+p.venue+'</p>'+
+        '<p class="edition">Edition '+p.edition+' · '+p.screens+'</p>'+
+      '</div>';
+    card.addEventListener('click', function(){ openLightbox(p); });
+    wall.appendChild(card);
+  });
+
+  /* ---------- SEPARATION CONTROLS ---------- */
+  const sepBtns = document.querySelectorAll('.controls .sep');
+  sepBtns.forEach(function(b){
+    b.addEventListener('click', function(){
+      sepBtns.forEach(function(x){x.classList.remove('active');x.setAttribute('aria-pressed','false');});
+      b.classList.add('active'); b.setAttribute('aria-pressed','true');
+      wall.setAttribute('data-view', b.dataset.sep);
+    });
+  });
+
+  /* ---------- REGISTRATION TOGGLE ---------- */
+  const regToggle = document.getElementById('regToggle');
+  regToggle.addEventListener('click', function(){
+    const on = wall.classList.toggle('show-reg');
+    regToggle.setAttribute('aria-pressed', on ? 'true':'false');
+  });
+
+  /* ---------- RE-PULL (animate screen print) ---------- */
+  const repull = document.getElementById('repullBtn');
+  repull.addEventListener('click', function(){
+    const cards = document.querySelectorAll('.poster-card');
+    cards.forEach(function(c){ c.classList.remove('printing'); });
+    // force reflow then re-add staggered
+    void wall.offsetWidth;
+    cards.forEach(function(c,i){
+      setTimeout(function(){ c.classList.add('printing'); }, i*120);
+    });
+  });
+
+  /* ---------- DENSITY SLIDER ---------- */
+  const density = document.getElementById('density');
+  density.addEventListener('input', function(){
+    wall.style.setProperty('--wall-cols', density.value);
+    density.setAttribute('aria-valuetext', density.value+' columns');
+  });
+
+  /* ---------- PROCESS DRAWER ---------- */
+  const drawer = document.getElementById('drawer');
+  const drawerOpen = document.getElementById('drawerOpen');
+  const drawerClose = document.getElementById('drawerClose');
+  function setDrawer(open){
+    drawer.classList.toggle('open', open);
+    drawer.setAttribute('aria-hidden', open ? 'false':'true');
+    drawerOpen.setAttribute('aria-expanded', open ? 'true':'false');
+    if(open) drawerClose.focus();
+  }
+  drawerOpen.addEventListener('click', function(){ setDrawer(!drawer.classList.contains('open')); });
+  drawerClose.addEventListener('click', function(){ setDrawer(false); drawerOpen.focus(); });
+
+  /* ---------- LIGHTBOX ---------- */
+  const lightbox = document.getElementById('lightbox');
+  const lbPoster = document.getElementById('lbPoster');
+  const lbClose = document.getElementById('lbClose');
+  let lastFocus = null;
+
+  function openLightbox(p){
+    lastFocus = document.activeElement;
+    lbPoster.innerHTML = posterSVG(p);
+    lbPoster.className = 'lb-poster';
+    document.getElementById('lbTitle').textContent = p.title;
+    document.getElementById('lbVenue').textContent = p.venue;
+    document.getElementById('lbCurator').textContent = p.curator;
+    document.getElementById('lbEdition').textContent = p.edition;
+    document.getElementById('lbDate').textContent = p.date;
+    document.getElementById('lbScreens').textContent = p.screens;
+    document.getElementById('lbMotif').textContent = p.motif;
+    // reset lb sep buttons
+    lbSeps.forEach(function(x){x.classList.remove('active');x.setAttribute('aria-pressed','false');});
+    lbSeps[0].classList.add('active'); lbSeps[0].setAttribute('aria-pressed','true');
+    applyLbView('all');
+    lightbox.classList.add('open');
+    document.body.style.overflow='hidden';
+    lbClose.focus();
+  }
+  function closeLightbox(){
+    lightbox.classList.remove('open');
+    document.body.style.overflow='';
+    if(lastFocus) lastFocus.focus();
+  }
+  lbClose.addEventListener('click', closeLightbox);
+  lightbox.addEventListener('click', function(e){ if(e.target===lightbox) closeLightbox(); });
+
+  // lightbox separation isolation
+  const lbSeps = Array.prototype.slice.call(document.querySelectorAll('[data-lb]'));
+  function applyLbView(v){
+    const svg = lbPoster.querySelector('svg');
+    if(!svg) return;
+    ['red','gold','mint','blue'].forEach(function(ch){
+      svg.querySelectorAll('.sep-'+ch).forEach(function(el){
+        el.style.opacity = (v==='all'||v===ch) ? '' : '0.06';
+      });
+    });
+  }
+  lbSeps.forEach(function(b){
+    b.addEventListener('click', function(){
+      lbSeps.forEach(function(x){x.classList.remove('active');x.setAttribute('aria-pressed','false');});
+      b.classList.add('active'); b.setAttribute('aria-pressed','true');
+      applyLbView(b.dataset.lb);
+    });
+  });
+
+  /* ---------- KEYBOARD ---------- */
+  document.addEventListener('keydown', function(e){
+    if(e.key==='Escape'){
+      if(lightbox.classList.contains('open')) closeLightbox();
+      else if(drawer.classList.contains('open')){ setDrawer(false); drawerOpen.focus(); }
+    }
+  });
+
+  // kick an initial print pull once loaded
+  window.addEventListener('load', function(){
+    setTimeout(function(){ repull.click(); repull.setAttribute('aria-pressed','false'); }, 400);
+  });
+
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/builds/long-strange-archive/cover.png b/builds/long-strange-archive/cover.png
new file mode 100644
index 0000000..6df31a0
Binary files /dev/null and b/builds/long-strange-archive/cover.png differ
diff --git a/builds/long-strange-archive/index.html b/builds/long-strange-archive/index.html
new file mode 100644
index 0000000..992e725
--- /dev/null
+++ b/builds/long-strange-archive/index.html
@@ -0,0 +1,289 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>The Long Strange Archive</title>
+  <style>
+    :root {
+      --bg: #f7efe6;
+      --surface: #fff9f1;
+      --text: #3a2c22;
+      --muted: #a08c7a;
+      --accent: #c65f38;
+      --accent-alt: #e0a458;
+      --card-radius: 14px;
+      --spacing: 4px 8px 12px 16px 24px 32px 48px 64px 96px 128px;
+      --font-display: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, serif;
+      --font-body: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+      --scale: 12px 16px 21px 28px 38px 50px 67px;
+    }
+
+    * {
+      box-sizing: border-box;
+      margin: 0;
+      padding: 0;
+    }
+
+    body {
+      background: var(--bg);
+      color: var(--text);
+      font-family: var(--font-body);
+      line-height: 1.6;
+      overflow-x: hidden;
+      scroll-behavior: smooth;
+      min-height: 100vh;
+      display: flex;
+      flex-direction: column;
+      font-size: var(--scale-1);
+    }
+
+    h1, h2, h3 {
+      font-family: var(--font-display);
+      font-weight: normal;
+    }
+
+    h1 {
+      font-size: var(--scale-5);
+      margin-bottom: var(--spacing-4);
+    }
+
+    h2 {
+      font-size: var(--scale-4);
+      margin-bottom: var(--spacing-3);
+    }
+
+    h3 {
+      font-size: var(--scale-3);
+      margin-bottom: var(--spacing-2);
+    }
+
+    .curator {
+      padding: var(--spacing-6) var(--spacing-4);
+      background: var(--surface);
+      box-shadow: 0 8px 24px rgba(0,0,0,0.12);
+      border-radius: var(--card-radius);
+      margin-bottom: var(--spacing-6);
+    }
+
+    .timeline {
+      display: flex;
+      overflow-x: auto;
+      padding: var(--spacing-4);
+      gap: var(--spacing-4);
+      scroll-behavior: smooth;
+      -webkit-overflow-scrolling: touch;
+    }
+
+    .timeline-item {
+      flex: 0 0 auto;
+      min-width: 120px;
+      padding: var(--spacing-2);
+      background: var(--muted);
+      border-radius: var(--card-radius);
+      text-align: center;
+      transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
+    }
+
+    .timeline-item:hover {
+      transform: scale(1.05);
+      box-shadow: 0 16px 48px rgba(0,0,0,0.18);
+    }
+
+    .artifact-station {
+      display: flex;
+      flex-direction: column;
+      gap: var(--spacing-4);
+      padding: var(--spacing-4);
+    }
+
+    .artifact {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-2);
+      padding: var(--spacing-2);
+      background: var(--surface);
+      border-radius: var(--card-radius);
+      box-shadow: 0 8px 24px rgba(0,0,0,0.12);
+      transition: box-shadow 0.2s ease-in-out;
+    }
+
+    .artifact:hover {
+      box-shadow: 0 16px 48px rgba(0,0,0,0.18);
+    }
+
+    .label {
+      font-size: var(--scale-0);
+      letter-spacing: 0.15em;
+      text-transform: uppercase;
+      color: var(--muted);
+      margin-bottom: var(--spacing-2);
+    }
+
+    .label-detail {
+      font-size: var(--scale-0);
+      color: var(--text);
+    }
+
+    .listening-room {
+      background: var(--surface);
+      padding: var(--spacing-6);
+      box-shadow: 0 8px 24px rgba(0,0,0,0.12);
+      border-radius: var(--card-radius);
+      margin-bottom: var(--spacing-6);
+    }
+
+    .spotlight {
+      background: var(--accent);
+      color: var(--surface);
+      padding: var(--spacing-4);
+      border-radius: var(--card-radius);
+      margin-bottom: var(--spacing-6);
+      text-align: center;
+    }
+
+    .venue-map {
+      background: var(--surface);
+      padding: var(--spacing-4);
+      box-shadow: 0 8px 24px rgba(0,0,0,0.12);
+      border-radius: var(--card-radius);
+      margin-bottom: var(--spacing-6);
+    }
+
+    .reduce-motion {
+      animation: none !important;
+    }
+
+    .fade-up {
+      animation: fadeUp 0.7s cubic-bezier(0.22,1,0.36,1) both;
+    }
+
+    @keyframes fadeUp {
+      from {
+        opacity: 0;
+        transform: translateY(28px);
+      }
+      to {
+        opacity: 1;
+        transform: none;
+      }
+    }
+
+    @media (prefers-reduced-motion: no-preference) {
+      body {
+        background: linear-gradient(180deg, var(--accent), var(--accent-alt)) fixed;
+        background-size: 400% 400%;
+        animation: drift 18s ease-in-out infinite;
+      }
+
+      @keyframes drift {
+        0% { background-position: 0% 50%; }
+        50% { background-position: 100% 50%; }
+        100% { background-position: 0% 50%; }
+      }
+    }
+
+    @media (max-width: 768px) {
+      .timeline {
+        overflow-x: auto;
+        flex-wrap: wrap;
+      }
+
+      .timeline-item {
+        flex: 1 1 80px;
+        min-width: 80px;
+      }
+    }
+  </style>
+</head>
+<body class="fade-up">
+  <h1>The Long Strange Archive</h1>
+  <div class="curator">
+    <h2>Curatorial Statement</h2>
+    <p>This exhibition traces the Grateful Dead’s visual and cultural legacy across five decades, from the psychedelic counterculture of the 1960s to the final performances of the 1990s. Through a curated selection of rare and overlooked artifacts, this space invites reflection on the band's evolution as artists, activists, and cultural icons. It is not a museum of nostalgia, but a scholarly exploration of a band that reshaped the boundaries of music, art, and community.</p>
+  </div>
+
+  <div class="timeline">
+    <div class="timeline-item">1965</div>
+    <div class="timeline-item">1967</div>
+    <div class="timeline-item">1971</div>
+    <div class="timeline-item">1977</div>
+    <div class="timeline-item">1987</div>
+    <div class="timeline-item">1995</div>
+    <div class="timeline-item">2009</div>
+  </div>
+
+  <div class="artifact-station">
+    <h2>Artifact Stations</h2>
+    <div class="artifact">
+      <div class="label">HANDBILL</div>
+      <div class="label-detail">Date: 1967 | Medium: Screen-printed paper | Provenance: San Francisco, CA</div>
+      <div class="label-detail">A handbill from a 1967 gig at the Avalon Ballroom, featuring a psychedelic design that mimics a cosmic landscape. The poster is a relic of the era’s underground printing movement, where artists created ephemera for music, politics, and protest.</div>
+    </div>
+
+    <div class="artifact">
+      <div class="label">TICKET STUB</div>
+      <div class="label-detail">Date: 1971 | Medium: Paper | Provenance: Fillmore West, San Francisco</div>
+      <div class="label-detail">This stub from a Fillmore West show reveals the band’s growing influence and the logistical scale of their concerts. The ticket’s design reflects the era’s mix of corporate production and rock-n-roll authenticity, a paradox that defined the Dead’s touring life.</div>
+    </div>
+
+    <div class="artifact">
+      <div class="label">BACKSTAGE EPHEMERA</div>
+      <div class="label-detail">Date: 1977 | Medium: Mixed media | Provenance: San Francisco, CA</div>
+      <div class="label-detail">A backstage pass and scribbled note from a 1977 tour, showing the informal, almost ritualistic nature of the Dead’s performances. The pass is marked with a hand-drawn mandala, a symbol of the band’s deep spiritual interests and the countercultural ethos of the time.</div>
+    </div>
+
+    <div class="artifact">
+      <div class="label">STAGE PHOTOGRAPHY</div>
+      <div class="label-detail">Date: 1987 | Medium: Silver gelatin print | Provenance: The Warfield, San Francisco</div>
+      <div class="label-detail">This photograph captures the band mid-performance at The Warfield, with members in full stage regalia and a crowd in motion. The image shows the Dead in their prime, a balance of musical mastery and visual spectacle that defined their later years.</div>
+    </div>
+
+    <div class="artifact">
+      <div class="label">FAN-MADE OBJECT</div>
+      <div class="label-detail">Date: 1995 | Medium: Mixed media | Provenance: Santa Cruz, CA</div>
+      <div class="label-detail">A handcrafted "Deadhead" pin made of wood and fabric, featuring an abstract representation of the band’s logo. This object represents the deeply personal and communal relationship between the band and its fans, a legacy that continues to this day.</div>
+    </div>
+  </div>
+
+  <div class="listening-room">
+    <h2>Listening Room</h2>
+    <audio controls>
+      <source src="https://upload.wikimedia.org/wikipedia/en/3/36/GratefulDead-PlayingInTheDark.ogg" type="audio/ogg">
+      Your browser does not support the audio element.
+    </audio>
+    <p>Audio from <i>Playing in the Dark</i> (1989), a live recording that captures the band's improvisational brilliance and the emotional resonance of their performances.</p>
+  </div>
+
+  <div class="spotlight">
+    <h3>From the Archive</h3>
+    <p><strong>Artifact:</strong> 1973 "Dark Star" Poster<br><strong>Medium:</strong> Hand-drawn ink and collage<br><strong>Provenance:</strong> Portland, OR</p>
+    <p>This rare poster from a 1973 "Dark Star" show is a masterwork of psychedelic printmaking. Featuring a spiral of ink and paper collage, it reflects the band’s deep engagement with art, spirituality, and the visual language of the era.</p>
+  </div>
+
+  <div class="venue-map">
+    <h2>Venue Map</h2>
+    <p>Explore a map of the Dead’s most iconic venues, including The Fillmore, The Warfield, and the outdoor festivals of the 1970s and 1980s. Each location is marked with a small icon and a brief historical note.</p>
+  </div>
+
+  <script>
+    // Keyboard navigation: 'Tab' to navigate, 'Enter' to open artifact details
+    const artifacts = document.querySelectorAll('.artifact');
+    artifacts.forEach((artifact, index) => {
+      artifact.tabIndex = 0;
+      artifact.addEventListener('keydown', function(e) {
+        if (e.key === 'Enter') {
+          alert('Artifact ' + (index + 1) + ' details: ' + this.querySelector('.label-detail').textContent);
+        }
+      });
+    });
+
+    // Reduce motion class toggle
+    if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
+      document.body.classList.remove('reduce-motion');
+    } else {
+      document.body.classList.add('reduce-motion');
+    }
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/builds/objects-golden-road/cover.png b/builds/objects-golden-road/cover.png
new file mode 100644
index 0000000..9a82bee
Binary files /dev/null and b/builds/objects-golden-road/cover.png differ
diff --git a/builds/objects-golden-road/index.html b/builds/objects-golden-road/index.html
new file mode 100644
index 0000000..dfd0497
--- /dev/null
+++ b/builds/objects-golden-road/index.html
@@ -0,0 +1,328 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title>Objects in the Golden Road</title>
+  <style>
+    :root {
+      --bg: #faf8f4;
+      --surface: #ffffff;
+      --text: #1c1a17;
+      --muted: #9b948a;
+      --accent1: #b0472e;
+      --accent2: #2f4c39;
+      --display-font: 'Playfair Display', Georgia, serif;
+      --body-font: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+      --caption: 12px;
+      --body: 16px;
+      --lead: 21px;
+      --h3: 28px;
+      --h2: 38px;
+      --h1: 50px;
+      --display: 67px;
+    }
+
+    * {
+      box-sizing: border-box;
+      margin: 0;
+      padding: 0;
+    }
+
+    body {
+      background-color: var(--bg);
+      color: var(--text);
+      font-family: var(--body-font);
+      line-height: 1.6;
+      min-height: 100vh;
+      display: flex;
+      flex-direction: column;
+    }
+
+    header {
+      padding: 96px 48px;
+      text-align: center;
+      background: linear-gradient(135deg, #faf8f4, #fff9f1);
+      box-shadow: 0 0 48px rgba(0,0,0,0.08);
+      animation: ambientDrift 18s ease-in-out infinite;
+    }
+
+    h1 {
+      font-family: var(--display-font);
+      font-size: var(--h1);
+      color: var(--accent1);
+      letter-spacing: 0.15em;
+      text-transform: uppercase;
+      margin-bottom: 8px;
+    }
+
+    h2 {
+      font-family: var(--display-font);
+      font-size: var(--h2);
+      color: var(--text);
+      margin-bottom: 4px;
+    }
+
+    p {
+      font-size: var(--body);
+      color: var(--muted);
+    }
+
+    main {
+      flex: 1;
+      padding: 48px;
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+      gap: 24px;
+    }
+
+    .card {
+      background-color: var(--surface);
+      padding: 24px;
+      border-radius: 14px;
+      box-shadow: 0 8px 24px rgba(0,0,0,0.12);
+      transition: box-shadow 150ms ease-in-out;
+    }
+
+    .card:hover {
+      box-shadow: 0 16px 48px rgba(0,0,0,0.18);
+    }
+
+    .title {
+      font-size: var(--h3);
+      font-family: var(--display-font);
+      margin-bottom: 12px;
+      color: var(--accent1);
+    }
+
+    .accession {
+      font-size: var(--caption);
+      color: var(--muted);
+      margin-bottom: 12px;
+    }
+
+    .label {
+      font-size: var(--caption);
+      color: var(--muted);
+      text-transform: uppercase;
+      letter-spacing: 0.15em;
+      margin-bottom: 8px;
+    }
+
+    .detail {
+      font-size: var(--body);
+      margin-bottom: 16px;
+    }
+
+    .curator {
+      font-size: var(--lead);
+      font-family: var(--display-font);
+      color: var(--text);
+      margin-top: 24px;
+    }
+
+    footer {
+      background-color: var(--accent1);
+      color: var(--surface);
+      padding: 24px 48px;
+      text-align: center;
+      font-size: var(--body);
+    }
+
+    @keyframes ambientDrift {
+      0% { background-position: 0% 50%; }
+      100% { background-position: 100% 50%; }
+    }
+
+    @keyframes fadeUp {
+      from {
+        opacity: 0;
+        transform: translateY(28px);
+      }
+      to {
+        opacity: 1;
+        transform: translateY(0);
+      }
+    }
+
+    .card {
+      animation: fadeUp 0.7s cubic-bezier(0.22,1,0.36,1) both;
+    }
+
+    .card:nth-child(1) { animation-delay: 0ms; }
+    .card:nth-child(2) { animation-delay: 90ms; }
+    .card:nth-child(3) { animation-delay: 180ms; }
+    .card:nth-child(4) { animation-delay: 270ms; }
+    .card:nth-child(5) { animation-delay: 360ms; }
+    .card:nth-child(6) { animation-delay: 450ms; }
+    .card:nth-child(7) { animation-delay: 540ms; }
+    .card:nth-child(8) { animation-delay: 630ms; }
+
+    .saved-tray, .collection-view, .compare-view, .zoom-view, .glossary, .print-mode {
+      display: none;
+    }
+
+    button {
+      background: var(--accent1);
+      color: var(--surface);
+      border: none;
+      padding: 12px 24px;
+      font-family: var(--display-font);
+      font-size: var(--caption);
+      text-transform: uppercase;
+      letter-spacing: 0.15em;
+      cursor: pointer;
+      transition: background 150ms ease-in-out;
+    }
+
+    button:hover {
+      background: #c65f38;
+    }
+
+    .zoomable {
+      cursor: zoom-in;
+    }
+  </style>
+</head>
+<body>
+  <header>
+    <h1>Objects in the Golden Road</h1>
+    <h2>A Curated Exhibition of Grateful Dead Fan Culture</h2>
+    <p>Presented as a museum artifact of memory, community, and the transcendent power of art.</p>
+  </header>
+
+  <main>
+    <div class="card">
+      <div class="title">Hand-Painted Road Case</div>
+      <div class="accession">Accession #001</div>
+      <div class="label">Materials</div>
+      <div class="detail">Hand-painted wood, gold leaf, and glass</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">120 cm × 80 cm × 30 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1990–1992</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Acquired from the estate of Jerry Garcia’s touring crew</div>
+      <div class="label">Condition</div>
+      <div class="detail">Excellent; minor wear consistent with use</div>
+      <div class="curator">This road case is a portal to the nomadic soul of a generation. Painted in the glow of late-night shows, it is both a vessel and a memory, carrying the weight of a journey that never truly ends.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Embroidered Tour Jacket</div>
+      <div class="accession">Accession #002</div>
+      <div class="label">Materials</div>
+      <div class="detail">Cotton canvas, silk thread</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">78 cm × 62 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1986–1988</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Gift from a long-time Deadhead to the museum’s collection</div>
+      <div class="label">Condition</div>
+      <div class="detail">Excellent; some fading of embroidery</div>
+      <div class="curator">The jacket is a tapestry of movement and memory, stitched with care by fans who wore it to every show, every festival, every celebration of life. It is not just clothing, but a map of belonging.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Annotated Cassette Box</div>
+      <div class="accession">Accession #003</div>
+      <div class="label">Materials</div>
+      <div class="detail">Cardboard, vinyl, handwritten notes</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">28 cm × 22 cm × 10 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1983–1985</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Collected by a fan from a private archive</div>
+      <div class="label">Condition</div>
+      <div class="detail">Fair; tapes intact, notes slightly faded</div>
+      <div class="curator">This box is a time capsule of a different era, one where music was carried in your hands, not on your phone. The annotations are a dialogue between the artist and the listener, a shared language of gratitude.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Pressed-Flower Notebook</div>
+      <div class="accession">Accession #004</div>
+      <div class="label">Materials</div>
+      <div class="detail">Paper, pressed flowers, ink</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">22 cm × 15 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1978–1980</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Donated by a family member of a long-time fan</div>
+      <div class="label">Condition</div>
+      <div class="detail">Good; some pages yellowed</div>
+      <div class="curator">Each page is a memory held in botanical form, a reminder that the spirit of the Dead lives on in the smallest details. This is a notebook of love, of time, of the beauty of impermanence.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Enamel Pin Board</div>
+      <div class="accession">Accession #005</div>
+      <div class="label">Materials</div>
+      <div class="detail">Wood, enamel pins</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">60 cm × 40 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1995–2000</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Acquired from a fan's personal collection</div>
+      <div class="label">Condition</div>
+      <div class="detail">Excellent; pins intact</div>
+      <div class="curator">This board is a mosaic of identity and fandom. Each pin is a story, a symbol, a piece of a shared journey. It is a visual language of community, one that transcends words.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Tie-Dyed Textile Study</div>
+      <div class="accession">Accession #006</div>
+      <div class="label">Materials</div>
+      <div class="detail">Cotton fabric, dye</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">110 cm × 80 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1975–1976</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Collected from a private archive of Grateful Dead memorabilia</div>
+      <div class="label">Condition</div>
+      <div class="detail">Excellent; vibrant colors</div>
+      <div class="curator">This textile is a celebration of spontaneity and expression. It is a canvas of chaos and harmony, a reflection of the improvisational spirit that defines the Grateful Dead and their followers.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Weathered Venue Sign</div>
+      <div class="accession">Accession #007</div>
+      <div class="label">Materials</div>
+      <div class="detail">Plywood, paint, weathering</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">150 cm × 100 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1973–1975</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Acquired from the original venue site</div>
+      <div class="label">Condition</div>
+      <div class="detail">Fair; weathered and aged</div>
+      <div class="curator">This sign is a relic of the early days, a testament to the raw energy of a scene that would grow into a global movement. Its weathering is a badge of honor, a marker of time and place.</div>
+    </div>
+
+    <div class="card">
+      <div class="title">Fan-Built Radio</div>
+      <div class="accession">Accession #008</div>
+      <div class="label">Materials</div>
+      <div class="detail">Wood, metal, vacuum tubes</div>
+      <div class="label">Dimensions</div>
+      <div class="detail">60 cm × 40 cm × 30 cm</div>
+      <div class="label">Date Range</div>
+      <div class="detail">1989–1991</div>
+      <div class="label">Provenance</div>
+      <div class="detail">Built by a fan and donated to the museum</div>
+      <div class="label">Condition</div>
+      <div class="detail">Good; functional with minor wear</div>
+      <div class="curator">This radio is a tribute to the spirit of DIY and the deep connection between art and audience. It is a device that brings people together, a symbol of the shared experience of music and memory.</div>
+    </div>
+  </main>
+
+  <footer>
+    <p>© 2025 Museum of Living Memory</p>
+  </footer>
+</body>
+</html>
\ No newline at end of file
diff --git a/builds/routes-residencies-rituals/cover.png b/builds/routes-residencies-rituals/cover.png
new file mode 100644
index 0000000..36187cf
Binary files /dev/null and b/builds/routes-residencies-rituals/cover.png differ
diff --git a/builds/routes-residencies-rituals/index.html b/builds/routes-residencies-rituals/index.html
new file mode 100644
index 0000000..3443f91
--- /dev/null
+++ b/builds/routes-residencies-rituals/index.html
@@ -0,0 +1,924 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Routes, Residencies &amp; Rituals — Geographies of a Traveling Music</title>
+<style>
+:root{
+  --navy:#0b1a2b; --navy2:#102437; --navy3:#16324a;
+  --paper:#f4ece0; --paper2:#ece0cd; --paper3:#e3d4bd;
+  --ink:#1b1611; --ink2:#463a2c;
+  --coral:#e8735e; --coral-soft:#d98a76;
+  --cyan:#3ac6d6; --cyan-deep:#1f8fa0;
+  --gold:#ffca3a; --amber:#f4a259; --green:#8ac926; --violet:#9b5de5;
+  --muted:#8a8578;
+  --west:#3ac6d6; --mtn:#f4a259; --mid:#8ac926; --ne:#9b5de5; --south:#e8735e;
+  --line:rgba(244,236,224,.14);
+  --shadow:0 8px 24px rgba(0,0,0,.28);
+  --shadow-h:0 16px 48px rgba(0,0,0,.42);
+  --serif:'Playfair Display','Didot','Bodoni MT',Georgia,'Times New Roman',serif;
+  --sans:-apple-system,'Segoe UI','Helvetica Neue',Arial,sans-serif;
+}
+*{box-sizing:border-box}
+html{scroll-behavior:smooth}
+body{
+  margin:0; background:var(--navy); color:var(--paper);
+  font-family:var(--sans); line-height:1.6; -webkit-font-smoothing:antialiased;
+  overflow-x:hidden;
+}
+.label{
+  font-family:var(--sans); text-transform:uppercase; letter-spacing:.15em;
+  font-size:12px; color:var(--muted); font-weight:600;
+}
+.wrap{max-width:1180px; margin:0 auto; padding:0 24px}
+section{padding:72px 0}
+h1,h2,h3{font-family:var(--serif); font-weight:700; line-height:1.1; margin:0}
+a{color:var(--cyan)}
+
+/* ---------- ambient / hero ---------- */
+.hero{
+  position:relative; padding:96px 0 64px; overflow:hidden;
+  background:
+    radial-gradient(1200px 600px at 15% -10%, rgba(58,198,214,.16), transparent 60%),
+    radial-gradient(900px 500px at 90% 0%, rgba(232,115,94,.14), transparent 60%),
+    linear-gradient(180deg,#081522,#0b1a2b 70%);
+}
+.hero::before{
+  content:""; position:absolute; inset:0; opacity:.5; pointer-events:none;
+  background:
+    repeating-linear-gradient(90deg,transparent 0 79px,var(--line) 79px 80px),
+    repeating-linear-gradient(0deg,transparent 0 79px,var(--line) 79px 80px);
+  -webkit-mask-image:radial-gradient(70% 70% at 50% 30%,#000,transparent);
+          mask-image:radial-gradient(70% 70% at 50% 30%,#000,transparent);
+  animation:drift 22s ease-in-out infinite alternate;
+}
+@keyframes drift{from{transform:translate3d(0,0,0)}to{transform:translate3d(-40px,20px,0)}}
+.kicker{display:flex; gap:14px; align-items:center; flex-wrap:wrap; margin-bottom:22px}
+.kicker .dotline{flex:1; height:1px; background:var(--line); min-width:40px}
+.hero h1{font-size:clamp(40px,7vw,74px); letter-spacing:-.5px}
+.hero h1 .amp{font-style:italic; color:var(--coral)}
+.hero .sub{
+  font-family:var(--serif); font-style:italic; font-size:clamp(19px,2.6vw,26px);
+  color:var(--paper2); max-width:760px; margin:20px 0 0;
+}
+.hero .lede{max-width:660px; margin:22px 0 0; color:#cdc4b4}
+.badges{display:flex; gap:10px; flex-wrap:wrap; margin-top:30px}
+.badge{
+  border:1px solid var(--line); border-radius:999px; padding:7px 14px;
+  font-size:12px; letter-spacing:.12em; text-transform:uppercase; color:var(--paper2);
+  background:rgba(255,255,255,.03);
+}
+.badge.warn{border-color:rgba(255,202,58,.5); color:var(--gold)}
+.spectral{
+  display:inline-block; height:6px; width:180px; border-radius:6px; margin-top:34px;
+  background:linear-gradient(90deg,var(--violet),var(--cyan),var(--green),var(--gold),var(--coral));
+}
+
+/* ---------- disclaimer ---------- */
+.note-illustrative{
+  border-left:3px solid var(--gold); background:rgba(255,202,58,.07);
+  padding:14px 18px; border-radius:8px; font-size:14.5px; color:#e9dcc6;
+}
+
+/* ---------- controls ---------- */
+.controls{
+  position:sticky; top:0; z-index:40;
+  background:rgba(8,20,33,.9); backdrop-filter:blur(10px);
+  border-bottom:1px solid var(--line); padding:14px 0;
+}
+.controls .wrap{display:flex; gap:22px 28px; align-items:center; flex-wrap:wrap}
+.ctl-group{display:flex; flex-direction:column; gap:7px}
+.ctl-group .label{font-size:11px}
+.chips{display:flex; gap:6px; flex-wrap:wrap}
+.chip{
+  cursor:pointer; border:1px solid var(--line); background:transparent; color:var(--paper2);
+  border-radius:999px; padding:6px 12px; font-size:12.5px; font-family:var(--sans);
+  letter-spacing:.03em; transition:all .18s ease;
+}
+.chip:hover{border-color:var(--paper2)}
+.chip[aria-pressed="true"]{background:var(--paper); color:var(--ink); border-color:var(--paper); font-weight:600}
+.chip .swatch{display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:6px; vertical-align:middle}
+.viewtabs{display:flex; border:1px solid var(--line); border-radius:10px; overflow:hidden}
+.viewtabs button{
+  cursor:pointer; background:transparent; border:0; color:var(--paper2);
+  padding:8px 16px; font-family:var(--sans); font-size:13px; letter-spacing:.04em;
+}
+.viewtabs button[aria-pressed="true"]{background:var(--cyan); color:#04222a; font-weight:700}
+.scrub{display:flex; align-items:center; gap:12px; min-width:260px; flex:1}
+.scrub input[type=range]{flex:1; accent-color:var(--coral); cursor:pointer}
+.yearbox{
+  font-family:var(--serif); font-size:30px; font-weight:700; color:var(--gold);
+  min-width:78px; text-align:center;
+}
+.playbtn{
+  cursor:pointer; display:inline-flex; align-items:center; gap:8px;
+  background:var(--coral); color:#2a0f09; border:0; border-radius:8px;
+  padding:9px 16px; font-weight:700; font-family:var(--sans); font-size:13px;
+  letter-spacing:.03em; transition:transform .15s ease, background .2s ease;
+}
+.playbtn:hover{transform:translateY(-1px); background:#ef8570}
+.playbtn .tri{font-size:12px}
+
+/* ---------- map ---------- */
+.mapstage{padding-top:40px}
+.stage-head{display:flex; justify-content:space-between; align-items:flex-end; gap:20px; flex-wrap:wrap; margin-bottom:20px}
+.stage-head h2{font-size:clamp(26px,4vw,38px)}
+.mapshell{
+  position:relative; border:1px solid var(--line); border-radius:16px; overflow:hidden;
+  background:linear-gradient(180deg,#0a1826,#0d2033); box-shadow:var(--shadow);
+}
+.mapgrid-bg{position:absolute; inset:0; opacity:.5; pointer-events:none;
+  background:
+   repeating-linear-gradient(90deg,transparent 0 59px,rgba(244,236,224,.05) 59px 60px),
+   repeating-linear-gradient(0deg,transparent 0 59px,rgba(244,236,224,.05) 59px 60px);
+}
+svg.usmap{display:block; width:100%; height:auto}
+.land{fill:rgba(244,236,224,.045); stroke:rgba(244,236,224,.28); stroke-width:1.1}
+.graticule{stroke:rgba(58,198,214,.10); stroke-width:.6}
+.city{cursor:pointer; transition:opacity .3s ease, transform .2s ease}
+.city.hidden{opacity:0; pointer-events:none}
+.city .halo{opacity:0; transition:opacity .25s ease}
+.city.active .halo{opacity:1}
+.city:hover .core, .city:focus .core{stroke:#fff; stroke-width:1.6}
+.city .core{stroke:rgba(11,26,43,.7); stroke-width:1}
+.city:focus{outline:none}
+.city:focus .halo{opacity:1}
+.citylabel{
+  font-family:var(--sans); font-size:9px; letter-spacing:.14em; text-transform:uppercase;
+  fill:#cdbfa8; pointer-events:none; font-weight:600;
+}
+.route{fill:none; stroke:var(--gold); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round;
+  filter:drop-shadow(0 0 6px rgba(255,202,58,.5));}
+.routeghost{fill:none; stroke:rgba(255,202,58,.18); stroke-width:2; stroke-dasharray:2 6; stroke-linecap:round}
+.marker{fill:#fff; filter:drop-shadow(0 0 8px rgba(255,255,255,.9))}
+.res-arc{fill:none; stroke-linecap:round; opacity:.55}
+
+/* tooltip */
+.tip{
+  position:absolute; z-index:20; pointer-events:none; opacity:0; transform:translateY(4px);
+  transition:opacity .15s ease, transform .15s ease; max-width:240px;
+  background:var(--paper); color:var(--ink); border-radius:10px; padding:12px 14px;
+  box-shadow:var(--shadow-h); font-size:13px;
+}
+.tip.show{opacity:1; transform:translateY(0)}
+.tip h4{font-family:var(--serif); font-size:17px; margin:0 0 3px}
+.tip .region-tag{font-size:10.5px; letter-spacing:.14em; text-transform:uppercase; font-weight:700}
+.tip dl{margin:8px 0 0; display:grid; grid-template-columns:auto 1fr; gap:2px 10px; font-size:12px}
+.tip dt{color:var(--muted); text-transform:uppercase; letter-spacing:.08em; font-size:10px; align-self:center}
+.tip dd{margin:0; color:var(--ink2); font-weight:600}
+.tip .il{margin-top:8px; font-size:10.5px; color:#8a7c63; font-style:italic}
+
+/* legend */
+.legend{display:flex; gap:26px 34px; flex-wrap:wrap; margin-top:22px; align-items:center}
+.legend .grp{display:flex; gap:14px; flex-wrap:wrap; align-items:center}
+.leg-item{display:flex; gap:8px; align-items:center; font-size:12.5px; color:var(--paper2)}
+.leg-dot{width:12px; height:12px; border-radius:50%}
+.leg-line{width:26px; height:0; border-top:2.4px solid var(--gold); box-shadow:0 0 6px rgba(255,202,58,.5)}
+.leg-size{display:flex; gap:5px; align-items:flex-end}
+.leg-size i{display:inline-block; background:var(--paper2); border-radius:50%}
+
+/* ---------- list / cards ---------- */
+.view{display:none}
+.view.on{display:block}
+.cardgrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:22px}
+.citycard{
+  background:var(--navy2); border:1px solid var(--line); border-radius:14px; padding:22px 22px 20px;
+  box-shadow:var(--shadow); transition:transform .2s ease, box-shadow .2s ease, border-color .2s ease;
+  position:relative; overflow:hidden;
+}
+.citycard::before{content:""; position:absolute; left:0; top:0; bottom:0; width:4px; background:var(--west)}
+.citycard:hover{transform:translateY(-4px); box-shadow:var(--shadow-h); border-color:rgba(244,236,224,.28)}
+.citycard .rtag{font-size:10.5px; letter-spacing:.14em; text-transform:uppercase; font-weight:700}
+.citycard h3{font-size:24px; margin:6px 0 2px}
+.citycard .venue{font-family:var(--serif); font-style:italic; color:var(--coral-soft); font-size:16px}
+.citycard .meta{display:flex; gap:20px; margin:16px 0 10px}
+.citycard .meta .m{display:flex; flex-direction:column}
+.citycard .meta .num{font-family:var(--serif); font-size:26px; font-weight:700}
+.citycard .bar{height:6px; border-radius:6px; background:rgba(244,236,224,.12); overflow:hidden; margin-top:4px}
+.citycard .bar i{display:block; height:100%; border-radius:6px}
+.citycard .note{color:#c3b9a7; font-size:13.5px; margin-top:12px}
+
+/* ---------- table ---------- */
+.tablewrap{overflow-x:auto; border:1px solid var(--line); border-radius:14px; background:var(--navy2)}
+table{border-collapse:collapse; width:100%; min-width:680px; font-size:14px}
+caption{text-align:left; padding:16px 18px; color:var(--muted); font-size:13px}
+th,td{padding:11px 16px; text-align:left; border-bottom:1px solid var(--line)}
+thead th{
+  position:sticky; top:0; background:var(--navy3); color:var(--paper);
+  text-transform:uppercase; letter-spacing:.1em; font-size:11px; font-weight:700;
+}
+tbody tr:hover{background:rgba(58,198,214,.06)}
+td .rd{width:9px; height:9px; border-radius:50%; display:inline-block; margin-right:8px; vertical-align:middle}
+.num-cell{font-variant-numeric:tabular-nums; font-weight:600}
+
+/* ---------- ribbons + seasonal ---------- */
+.panel{background:linear-gradient(180deg,#0a1725,#0c1e30); border-top:1px solid var(--line); border-bottom:1px solid var(--line)}
+.panel h2{font-size:clamp(26px,4vw,38px)}
+.panel .intro{max-width:720px; color:#cdc4b4; margin:14px 0 30px}
+.vizshell{border:1px solid var(--line); border-radius:14px; background:rgba(0,0,0,.16); padding:20px 12px 8px; overflow-x:auto; box-shadow:var(--shadow)}
+svg.ribbon,svg.seasonal{display:block; width:100%; min-width:720px; height:auto}
+.ribbon text.vlabel{font-family:var(--serif); font-size:13px; fill:var(--paper2); font-style:italic}
+.ribbon text.vcity{font-family:var(--sans); font-size:9.5px; letter-spacing:.1em; text-transform:uppercase; fill:var(--muted)}
+.ribbon text.axis, .seasonal text.axis{font-family:var(--sans); font-size:10px; letter-spacing:.12em; text-transform:uppercase; fill:var(--muted)}
+.ribbon .rib{cursor:default; transition:opacity .2s ease}
+.ribbon .rib:hover{opacity:.85}
+.seasonal text.rlab{font-family:var(--sans); font-size:11px; letter-spacing:.06em; fill:var(--paper2)}
+.seasonal text.mo{font-family:var(--sans); font-size:9.5px; fill:var(--muted); text-transform:uppercase; letter-spacing:.08em}
+
+/* ---------- essays ---------- */
+.essays{background:var(--paper); color:var(--ink)}
+.essays .label{color:#9a8d78}
+.essays h2{color:var(--ink); font-size:clamp(28px,4.4vw,42px)}
+.essays .intro{color:var(--ink2); max-width:720px; margin:16px 0 44px; font-size:17px}
+.essaygrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr)); gap:34px 40px}
+.essay{position:relative}
+.essay .no{font-family:var(--serif); font-size:15px; color:var(--coral); letter-spacing:.1em; font-weight:700}
+.essay h3{font-size:26px; margin:6px 0 4px; color:var(--ink)}
+.essay .dek{font-family:var(--serif); font-style:italic; color:#6b5b45; font-size:16px; margin-bottom:12px}
+.essay p{margin:0 0 12px; color:#40362a; font-size:15.5px}
+.essay p:first-letter{}
+.essay.lead p:first-of-type:first-letter{
+  font-family:var(--serif); font-size:56px; float:left; line-height:.8; padding:6px 10px 0 0; color:var(--coral);
+}
+.essay .rule{height:1px; background:linear-gradient(90deg,var(--coral),transparent); margin:0 0 16px; width:120px}
+
+/* ---------- footer ---------- */
+footer{background:#07131f; padding:56px 0 44px; border-top:1px solid var(--line)}
+footer .cols{display:flex; justify-content:space-between; gap:30px; flex-wrap:wrap; align-items:flex-start}
+footer h3{font-size:22px; margin-bottom:8px}
+footer p{color:#a99e8c; font-size:13.5px; max-width:520px}
+footer .spectral{margin-top:20px}
+
+@media (max-width:640px){
+  section{padding:52px 0}
+  .controls{position:static}
+  .hero h1 .amp{display:inline}
+  .legend{gap:16px}
+  .citycard .meta{gap:14px}
+}
+.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); border:0}
+</style>
+</head>
+<body>
+
+<!-- ============ HERO ============ -->
+<header class="hero">
+  <div class="wrap">
+    <div class="kicker">
+      <span class="label">A Data-Driven Cultural Feature</span>
+      <span class="dotline"></span>
+      <span class="label">Fictionalized · Illustrative</span>
+    </div>
+    <h1>Routes, Residencies <span class="amp">&amp;</span> Rituals</h1>
+    <p class="sub">The geography and recurring patterns of a traveling musical community — how a caravan of players and pilgrims wrote itself across a continent.</p>
+    <p class="lede">For three decades a band and its congregation kept moving: the same rooms in the same seasons, the same rites in every parking lot. This feature reads that movement as geography — a stylized map, a set of venue ribbons, and five short essays on the culture that formed in the aisles and the asphalt.</p>
+    <div class="badges">
+      <span class="badge warn">All numbers illustrative</span>
+      <span class="badge">27 stylized cities</span>
+      <span class="badge">Map · List · Table</span>
+      <span class="badge">Route playback</span>
+    </div>
+    <span class="spectral" aria-hidden="true"></span>
+  </div>
+</header>
+
+<!-- ============ CONTROLS ============ -->
+<div class="controls" role="region" aria-label="Gallery controls">
+  <div class="wrap">
+    <div class="viewtabs" role="tablist" aria-label="View mode">
+      <button role="tab" data-view="map" aria-pressed="true">Map</button>
+      <button role="tab" data-view="list" aria-pressed="false">List</button>
+      <button role="tab" data-view="table" aria-pressed="false">Table</button>
+    </div>
+
+    <div class="ctl-group">
+      <span class="label" id="regionlbl">Filter by region</span>
+      <div class="chips" role="group" aria-labelledby="regionlbl" id="regionChips">
+        <button class="chip" data-region="all" aria-pressed="true">All</button>
+        <button class="chip" data-region="West" aria-pressed="false"><span class="swatch" style="background:var(--west)"></span>West</button>
+        <button class="chip" data-region="Mountain" aria-pressed="false"><span class="swatch" style="background:var(--mtn)"></span>Mountain &amp; SW</button>
+        <button class="chip" data-region="Midwest" aria-pressed="false"><span class="swatch" style="background:var(--mid)"></span>Midwest</button>
+        <button class="chip" data-region="South" aria-pressed="false"><span class="swatch" style="background:var(--south)"></span>South</button>
+        <button class="chip" data-region="Northeast" aria-pressed="false"><span class="swatch" style="background:var(--ne)"></span>Northeast</button>
+      </div>
+    </div>
+
+    <div class="ctl-group scrub">
+      <span class="label" id="yearlbl">Year scrubber &amp; route</span>
+      <div class="scrub" aria-labelledby="yearlbl">
+        <input type="range" id="yearRange" min="1965" max="1995" step="1" value="1977"
+               aria-label="Select year" aria-valuetext="1977">
+        <span class="yearbox" id="yearBox">1977</span>
+        <button class="playbtn" id="playBtn" aria-pressed="false">
+          <span class="tri" id="playIcon">▶</span><span id="playTxt">Play route</span>
+        </button>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- ============ MAP VIEW ============ -->
+<section class="mapstage">
+  <div class="wrap">
+
+    <div class="view on" id="view-map">
+      <div class="stage-head">
+        <div>
+          <span class="label">The Continent as a Setlist</span>
+          <h2>A Stylized Tour Map</h2>
+        </div>
+        <p class="label" id="tourCaption" style="max-width:340px; text-align:right">Route shown: <strong style="color:var(--gold)">1977 leg</strong> — press play to trace it</p>
+      </div>
+
+      <div class="mapshell">
+        <div class="mapgrid-bg" aria-hidden="true"></div>
+        <div class="tip" id="tip" role="status" aria-live="polite"></div>
+        <svg class="usmap" id="usmap" viewBox="0 0 990 600" role="group" aria-label="Stylized United States map of tour cities. A data table alternative is available in the Table view.">
+          <!-- graticule -->
+          <g class="graticule" aria-hidden="true">
+            <line x1="0" y1="150" x2="990" y2="150"/><line x1="0" y1="300" x2="990" y2="300"/><line x1="0" y1="450" x2="990" y2="450"/>
+            <line x1="250" y1="0" x2="250" y2="600"/><line x1="500" y1="0" x2="500" y2="600"/><line x1="750" y1="0" x2="750" y2="600"/>
+          </g>
+          <!-- stylized landmass -->
+          <path class="land" d="M55,73 L62,206 L68,268 L88,293 L111,352 L169,400 L218,404 L332,418 L386,477 L439,508 L470,539 L508,477 L584,473 L675,460 L695,502 L732,556 L721,477 L752,398 L805,341 L813,300 L828,248 L859,220 L889,200 L935,143 L920,95 L798,143 L691,127 L508,60 L370,60 L81,60 Z"/>
+          <g class="res-layer" id="resLayer" aria-hidden="true"></g>
+          <path class="routeghost" id="routeGhost" d=""></path>
+          <path class="route" id="routePath" d=""></path>
+          <g id="cityLayer"></g>
+          <circle class="marker" id="marker" r="4.5" cx="-20" cy="-20" aria-hidden="true"></circle>
+        </svg>
+      </div>
+
+      <div class="legend" aria-label="Map legend">
+        <div class="grp">
+          <span class="label" style="font-size:11px">Region</span>
+          <span class="leg-item"><span class="leg-dot" style="background:var(--west)"></span>West</span>
+          <span class="leg-item"><span class="leg-dot" style="background:var(--mtn)"></span>Mountain &amp; SW</span>
+          <span class="leg-item"><span class="leg-dot" style="background:var(--mid)"></span>Midwest</span>
+          <span class="leg-item"><span class="leg-dot" style="background:var(--south)"></span>South</span>
+          <span class="leg-item"><span class="leg-dot" style="background:var(--ne)"></span>Northeast</span>
+        </div>
+        <div class="grp">
+          <span class="leg-item"><span class="leg-line"></span>Animated route leg</span>
+          <span class="leg-item"><span class="leg-size"><i style="width:6px;height:6px"></i><i style="width:10px;height:10px"></i><i style="width:15px;height:15px"></i></span>&nbsp;Illustrative show density</span>
+          <span class="leg-item"><svg width="26" height="14"><path d="M2,12 Q13,-2 24,12" fill="none" stroke="var(--violet)" stroke-width="2.4" opacity=".6"/></svg>Residency arc</span>
+        </div>
+      </div>
+
+      <p class="note-illustrative" style="margin-top:26px">
+        <strong>A note on the numbers.</strong> Every figure here — show counts, residency nights, seasonal intensities, route order — is <em>illustrative sample content</em> invented to demonstrate how a traveling music community <em>might</em> be explored. Nothing on this page should be read as a verified statistic. City positions are stylized, not surveyed.
+      </p>
+    </div>
+
+    <!-- ============ LIST VIEW ============ -->
+    <div class="view" id="view-list">
+      <div class="stage-head">
+        <div><span class="label">The Rooms, One by One</span><h2>City Cards</h2></div>
+        <p class="label" style="max-width:340px;text-align:right" id="listCount"></p>
+      </div>
+      <div class="cardgrid" id="cardGrid"></div>
+    </div>
+
+    <!-- ============ TABLE VIEW ============ -->
+    <div class="view" id="view-table">
+      <div class="stage-head">
+        <div><span class="label">Accessible Data Fallback</span><h2>Full Data Table</h2></div>
+      </div>
+      <div class="tablewrap">
+        <table id="dataTable">
+          <caption>Illustrative touring data by city — filtered by the region chips and the year scrubber above. All values are fictional sample content.</caption>
+          <thead>
+            <tr>
+              <th scope="col">City</th><th scope="col">Region</th><th scope="col">Anchor venue</th>
+              <th scope="col">First year (illus.)</th><th scope="col">Shows (illus.)</th><th scope="col">Character</th>
+            </tr>
+          </thead>
+          <tbody id="tableBody"></tbody>
+        </table>
+      </div>
+    </div>
+
+  </div>
+</section>
+
+<!-- ============ RESIDENCY RIBBONS ============ -->
+<section class="panel">
+  <div class="wrap">
+    <span class="label">Where the Caravan Rested</span>
+    <h2>Venue-Residency Ribbons</h2>
+    <p class="intro">Certain rooms became annual rites: a band that toured constantly still <em>returned</em>, booking multi-night stands in the same halls year after year. Each ribbon below spans a venue's illustrative active window; its thickness suggests the accumulated weight of nights played there. Hover a ribbon for detail.</p>
+    <div class="vizshell">
+      <svg class="ribbon" id="ribbonSvg" viewBox="0 0 1000 430" role="img" aria-label="Ribbon chart of illustrative venue residencies across years 1965 to 1996. Thicker and longer ribbons indicate more accumulated nights. Values are illustrative.">
+      </svg>
+    </div>
+  </div>
+</section>
+
+<!-- ============ SEASONAL BANDS ============ -->
+<section>
+  <div class="wrap">
+    <span class="label">The Year Has a Shape</span>
+    <h2 style="font-size:clamp(26px,4vw,38px)">Seasonal Movement Bands</h2>
+    <p class="intro" style="color:#cdc4b4;max-width:720px;margin:14px 0 26px">
+      A touring life obeys weather and ritual. Winter pulls the community home to the Bay for the turning of the year; spring greens the Northeast; summer opens the mountains and stadiums; autumn folds back through the Midwest. The heat-band below reads this illustrative rhythm — darker cells mean a region drew more of the year's attention that month.
+    </p>
+    <div class="vizshell">
+      <svg class="seasonal" id="seasonalSvg" viewBox="0 0 1000 340" role="img" aria-label="Heat-band chart showing illustrative seasonal touring emphasis by region across twelve months. Darker cells indicate greater emphasis. Values are illustrative.">
+      </svg>
+    </div>
+  </div>
+</section>
+
+<!-- ============ ESSAYS ============ -->
+<section class="essays">
+  <div class="wrap">
+    <span class="label">Five Short Essays</span>
+    <h2>The Culture in the Aisles &amp; the Asphalt</h2>
+    <p class="intro">Maps and ribbons chart the <em>where</em>. These pieces reach for the <em>why</em> — the humane, recurring patterns that turned a concert tour into a moving town. They are interpretive sample essays, not reportage.</p>
+
+    <div class="essaygrid">
+      <article class="essay lead">
+        <div class="rule"></div>
+        <span class="no">Ⅰ · Pilgrimage</span>
+        <h3>The Road as Devotion</h3>
+        <p class="dek">Movement as a form of belief.</p>
+        <p>To follow a band across a summer is to convert distance into meaning. The route becomes a rosary — each city a bead, each night a station on a circuit that repeats until repetition itself feels like prayer. People quit jobs, sold vans, slept in fields, not to arrive but to keep arriving.</p>
+        <p>What pilgrimage offers is a structure for wonder: a fixed liturgy (the doors, the tuning, the first chord) inside an unfixed landscape. The sameness of the ritual makes the strangeness of the geography bearable, and the strangeness of the geography keeps the ritual from calcifying. Devotion, here, is simply the willingness to go again.</p>
+      </article>
+
+      <article class="essay">
+        <div class="rule"></div>
+        <span class="no">Ⅱ · Parking-Lot Exchange</span>
+        <h3>An Economy of Gift</h3>
+        <p class="dek">Shakedown Street as marketplace and commons.</p>
+        <p>Before the doors opened, a second show ran in the lot: grilled cheese and hand-screened shirts, stickers and stones, whispered directions to the next town. It looked like commerce and behaved like a gift economy — prices soft, barter common, the point less profit than participation.</p>
+        <p>The lot was where a diffuse crowd became a neighborhood for an afternoon. You could enter with nothing and leave fed, housed for the night, folded into a caravan. Its illustrative currency was reputation and reciprocity, and its real product was belonging — the sense that showing up was itself a contribution.</p>
+      </article>
+
+      <article class="essay">
+        <div class="rule"></div>
+        <span class="no">Ⅲ · Taping Culture</span>
+        <h3>The Archive Made of Air</h3>
+        <p class="dek">Permission, and the citizen archive it built.</p>
+        <p>A section behind the soundboard bristling with microphones did something quietly radical: it treated the audience as archivists. Tapes were traded, never sold — dubbed onto cassettes, mailed in padded envelopes, ranked by the warmth of a particular night's second set.</p>
+        <p>Because every show was improvised, no two were interchangeable, and the archive mattered: a citizen record of a music that refused to sit still. Taping turned ephemerality into inheritance. The performance vanished into the air the moment it ended, and a network of strangers spent decades catching it before it fell.</p>
+      </article>
+
+      <article class="essay">
+        <div class="rule"></div>
+        <span class="no">Ⅳ · Regional Identity</span>
+        <h3>A Nation of Local Weathers</h3>
+        <p class="dek">The same band, refracted by place.</p>
+        <p>A tour reveals a country as a set of local weathers — social as much as meteorological. A Bay Area crowd carried the founding myth; a Northeast run in spring felt collegiate and dense; a mountain night in summer was thinner, higher, stranger; a Southern date moved to a different humidity of attention.</p>
+        <p>The band was a constant thrown against these variables, and the interaction produced identity. Regions "owned" certain nights the way towns own festivals, and returning acts learned each place's temperament — where to stretch a jam, where to lean on the anthem. Geography, it turns out, has taste.</p>
+      </article>
+
+      <article class="essay">
+        <div class="rule"></div>
+        <span class="no">Ⅴ · Venue &amp; Set</span>
+        <h3>The Room Writes the Setlist</h3>
+        <p class="dek">How architecture becomes music.</p>
+        <p>A cavernous arena, a red-rock amphitheatre, a ski-hill lawn at dusk — each room proposes a different music. Reverb, sightlines, altitude, the way sound returns off stone all press on the choices a band makes in the moment. The setlist is partly written by the walls.</p>
+        <p>Residencies deepened this: play the same hall enough years and the room becomes a collaborator with memory of its own. Openers that only work outdoors, closers that need a low ceiling, a ballad that belongs to one particular balcony. The illustrative lesson of the ribbons above is that <em>where</em> a night happens is never neutral — place is an instrument.</p>
+      </article>
+    </div>
+  </div>
+</section>
+
+<!-- ============ FOOTER ============ -->
+<footer>
+  <div class="wrap">
+    <div class="cols">
+      <div>
+        <span class="label">Routes, Residencies &amp; Rituals</span>
+        <h3>A stylized data story</h3>
+        <p>An exhibited cultural-journalism feature built as a single, self-contained page. Everything here — the map, the ribbons, the seasonal bands, and the figures inside the tooltips — is <strong>fictionalized, illustrative sample content</strong> designed to show <em>how</em> a traveling musical community might be read as geography. It asserts no verified statistics and names no real dates.</p>
+      </div>
+      <div>
+        <span class="label">Legend of colors</span>
+        <p style="margin-top:8px">Deep navy for the ground beneath the map · warm paper for the essays · faded coral for the pilgrims · signal cyan for the routes we trace · and small flashes of spectral color where a region insists on being itself.</p>
+      </div>
+    </div>
+    <span class="spectral" aria-hidden="true"></span>
+  </div>
+</footer>
+
+<script>
+(function(){
+"use strict";
+
+/* ---------------- DATA (all illustrative) ---------------- */
+var REGION_COLOR = {West:'#3ac6d6', Mountain:'#f4a259', Midwest:'#8ac926', South:'#e8735e', Northeast:'#9b5de5'};
+var REGION_LABEL = {West:'West', Mountain:'Mountain & SW', Midwest:'Midwest', South:'South', Northeast:'Northeast'};
+
+var CITIES = [
+ {id:'sf', name:'San Francisco', region:'West', x:89, y:293, venue:'Winterland', year:1965, shows:92, note:'The hearth. New Year runs and the founding congregation.', label:1},
+ {id:'oak',name:'Oakland', region:'West', x:98, y:299, venue:'Oakland Coliseum', year:1979, shows:40, note:'Later years’ Bay Area anchor for the turning of the year.', label:0},
+ {id:'la', name:'Los Angeles', region:'West', x:153, y:372, venue:'The Forum / Shrine', year:1966, shows:55, note:'A warmer, larger congregation down the coast.', label:1},
+ {id:'eug',name:'Eugene', region:'West', x:76, y:163, venue:'Field near Veneta', year:1972, shows:12, note:'Legendary hot-afternoon outdoor rites in the Oregon fields.', label:0},
+ {id:'pdx',name:'Portland', region:'West', x:85, y:133, venue:'Memorial Coliseum', year:1972, shows:18, note:'A dependable Northwest stop on the westerly loop.', label:0},
+ {id:'sea',name:'Seattle', region:'West', x:91, y:89, venue:'Center Arena', year:1973, shows:15, note:'The far corner — top of the map, edge of the circuit.', label:1},
+ {id:'den',name:'Denver', region:'Mountain', x:352, y:255, venue:'Red Rocks', year:1967, shows:30, note:'Red-rock amphitheatre nights, thin mountain air, long jams.', label:1},
+ {id:'tel',name:'Telluride', region:'Mountain', x:312, y:290, venue:'Town Park', year:1987, shows:4, note:'A rare high-altitude box-canyon rite; whispered-about.', label:0},
+ {id:'lv', name:'Las Vegas', region:'Mountain', x:200, y:327, venue:'Silver Bowl', year:1983, shows:14, note:'Late-era desert stadium runs; heat and neon.', label:1},
+ {id:'phx',name:'Phoenix', region:'Mountain', x:247, y:384, venue:'Compton Terrace', year:1980, shows:9, note:'Southwestern spring dates before the heat closes in.', label:0},
+ {id:'chi',name:'Chicago', region:'Midwest', x:620, y:208, venue:'Soldier Field', year:1971, shows:45, note:'The great Midwestern hinge; stadium summers and civic halls.', label:1},
+ {id:'alp',name:'Alpine Valley', region:'Midwest', x:607, y:193, venue:'Alpine Valley', year:1980, shows:24, note:'Ski-hill lawn amphitheatre — a summer residency of its own.', label:1},
+ {id:'min',name:'Minneapolis', region:'Midwest', x:534, y:144, venue:'Civic Center', year:1971, shows:12, note:'Upper-Midwest anchor on the northern swing.', label:0},
+ {id:'stl',name:'St. Louis', region:'Midwest', x:581, y:276, venue:'Fox / Kiel', year:1971, shows:16, note:'River-city rooms; a crossroads on the way east.', label:0},
+ {id:'det',name:'Detroit', region:'Midwest', x:690, y:199, venue:'Cobo / Pontiac', year:1971, shows:20, note:'Industrial Midwest stop with a loyal, loud crowd.', label:1},
+ {id:'buf',name:'Buffalo', region:'Northeast', x:754, y:187, venue:'Memorial Aud.', year:1970, shows:14, note:'Cold-weather northeastern room; a spring gateway.', label:0},
+ {id:'ith',name:'Ithaca', region:'Northeast', x:790, y:197, venue:'Barton Hall', year:1977, shows:3, note:'A single celebrated campus gymnasium night, endlessly retold.', label:0},
+ {id:'nyc',name:'New York', region:'Northeast', x:828, y:233, venue:'Madison Square Garden', year:1967, shows:70, note:'The eastern cathedral; the archetypal multi-night residency.', label:1},
+ {id:'phl',name:'Philadelphia', region:'Northeast', x:810, y:249, venue:'The Spectrum', year:1968, shows:52, note:'A perennial northeastern stand across two decades.', label:1},
+ {id:'dc', name:'Washington', region:'Northeast', x:782, y:270, venue:'Capital Centre', year:1970, shows:30, note:'Mid-Atlantic hub; suburban arena residencies.', label:1},
+ {id:'bos',name:'Boston', region:'Northeast', x:873, y:198, venue:'Boston Garden', year:1969, shows:44, note:'Old-barn acoustics; a college-town devotion.', label:1},
+ {id:'hfd',name:'Hartford', region:'Northeast', x:848, y:211, venue:'Civic Center', year:1977, shows:26, note:'Reliable New England civic-hall stand.', label:0},
+ {id:'atl',name:'Atlanta', region:'South', x:670, y:378, venue:'The Omni / Fox', year:1969, shows:22, note:'The Southern capital of the circuit; humid attention.', label:1},
+ {id:'nol',name:'New Orleans', region:'South', x:583, y:457, venue:'City Park', year:1970, shows:10, note:'A different rhythm entirely; the deep-south outlier.', label:1},
+ {id:'mia',name:'Miami', region:'South', x:734, y:544, venue:'Sportatorium', year:1974, shows:8, note:'The far tip of the peninsula — end of the line, south.', label:1},
+ {id:'aus',name:'Austin', region:'South', x:466, y:450, venue:'Manor Downs', year:1972, shows:6, note:'A Texas hill-country stop with its own local weather.', label:1},
+ {id:'eng',name:'Englishtown', region:'Northeast', x:825, y:239, venue:'Raceway', year:1977, shows:2, note:'A one-off megashow on a raceway; folklore-scale crowd.', label:0}
+];
+var CITY_BY_ID = {}; CITIES.forEach(function(c){CITY_BY_ID[c.id]=c;});
+
+/* Anchor tour legs (illustrative order) */
+var TOURS = {
+ 1970:['sf','buf','bos','phl','nyc','dc'],
+ 1972:['sf','pdx','den','min','chi','det','nyc'],
+ 1974:['sf','la','pdx','sea','eug'],
+ 1977:['sf','ith','buf','hfd','bos','phl','nyc','eng'],
+ 1980:['sf','nol','atl','dc','phl','nyc','bos'],
+ 1982:['sf','den','alp','chi','stl'],
+ 1985:['sf','oak','tel','den','chi'],
+ 1987:['sf','tel','alp','chi','phl','nyc','bos'],
+ 1989:['sf','lv','phx','den','chi','atl','phl','nyc'],
+ 1991:['oak','lv','den','alp','chi','bos','nyc','dc'],
+ 1993:['sf','lv','phx','den','chi','dc','phl','nyc'],
+ 1995:['sf','lv','stl','chi','det','nyc','bos']
+};
+var ANCHORS = Object.keys(TOURS).map(Number).sort(function(a,b){return a-b;});
+function tourForYear(y){ var pick=ANCHORS[0]; for(var i=0;i<ANCHORS.length;i++){ if(ANCHORS[i]<=y) pick=ANCHORS[i]; } return pick; }
+
+/* Residency ribbons (illustrative) */
+var VENUES = [
+ {v:'Winterland', city:'San Francisco', region:'West', s:1966, e:1978, nights:62},
+ {v:'Fillmore West', city:'San Francisco', region:'West', s:1968, e:1971, nights:26},
+ {v:'Red Rocks', city:'Denver', region:'Mountain', s:1972, e:1978, nights:20},
+ {v:'The Spectrum', city:'Philadelphia', region:'Northeast', s:1968, e:1989, nights:45},
+ {v:'Madison Square Garden', city:'New York', region:'Northeast', s:1979, e:1994, nights:58},
+ {v:'Capital Centre', city:'Washington', region:'Northeast', s:1973, e:1988, nights:29},
+ {v:'Boston Garden', city:'Boston', region:'Northeast', s:1979, e:1994, nights:41},
+ {v:'Alpine Valley', city:'East Troy, WI', region:'Midwest', s:1980, e:1989, nights:24},
+ {v:'Oakland Coliseum', city:'Oakland', region:'West', s:1979, e:1994, nights:31},
+ {v:'Frost Amphitheatre', city:'Stanford', region:'West', s:1982, e:1989, nights:17}
+];
+
+/* Seasonal emphasis matrix (illustrative, 0-1) */
+var SEASON = {
+ West:      [.80,.50,.32,.30,.42,.70,.60,.62,.70,.40,.30,.90],
+ Mountain:  [.10,.10,.20,.32,.52,.82,.72,.62,.50,.22,.10,.10],
+ Midwest:   [.10,.12,.22,.34,.44,.72,.84,.74,.60,.42,.20,.12],
+ South:     [.22,.32,.55,.62,.42,.20,.10,.10,.32,.52,.62,.30],
+ Northeast: [.20,.30,.60,.84,.60,.40,.32,.42,.72,.84,.60,.30]
+};
+var MONTHS=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
+
+/* ---------------- STATE ---------------- */
+var state = { year:1977, region:'all', view:'map' };
+var SVGNS='http://www.w3.org/2000/svg';
+var $=function(s){return document.querySelector(s);};
+
+/* ---------------- MAP: build cities + residency arcs ---------------- */
+var cityLayer=$('#cityLayer'), resLayer=$('#resLayer');
+
+// residency arcs between residency venue-cities (visual flourish)
+VENUES.forEach(function(vn){
+  var c = CITIES.filter(function(c){return c.venue.indexOf(vn.v)>=0 || c.name===vn.city;})[0];
+  if(!c) return;
+  var span = vn.e - vn.s;
+  var r = 14 + span*1.6;
+  var col = REGION_COLOR[vn.region];
+  var p=document.createElementNS(SVGNS,'path');
+  var x=c.x, y=c.y;
+  p.setAttribute('d','M'+(x-r)+','+y+' A'+r+','+r+' 0 0 1 '+(x+r)+','+y);
+  p.setAttribute('class','res-arc');
+  p.setAttribute('stroke',col);
+  p.setAttribute('stroke-width', Math.max(2, vn.nights/14));
+  resLayer.appendChild(p);
+});
+
+CITIES.forEach(function(c){
+  var g=document.createElementNS(SVGNS,'g');
+  g.setAttribute('class','city');
+  g.setAttribute('data-id',c.id);
+  g.setAttribute('data-region',c.region);
+  g.setAttribute('tabindex','0');
+  g.setAttribute('role','img');
+  g.setAttribute('aria-label', c.name+', '+REGION_LABEL[c.region]+'. Anchor venue '+c.venue+'. Illustrative first year '+c.year+', illustrative '+c.shows+' shows.');
+  var r = 3 + c.shows/70*6.5;
+  var halo=document.createElementNS(SVGNS,'circle');
+  halo.setAttribute('class','halo'); halo.setAttribute('cx',c.x); halo.setAttribute('cy',c.y);
+  halo.setAttribute('r', r+7); halo.setAttribute('fill','none');
+  halo.setAttribute('stroke',REGION_COLOR[c.region]); halo.setAttribute('stroke-width','1.5'); halo.setAttribute('opacity','.6');
+  var core=document.createElementNS(SVGNS,'circle');
+  core.setAttribute('class','core'); core.setAttribute('cx',c.x); core.setAttribute('cy',c.y);
+  core.setAttribute('r', r); core.setAttribute('fill',REGION_COLOR[c.region]);
+  g.appendChild(halo); g.appendChild(core);
+  if(c.label){
+    var t=document.createElementNS(SVGNS,'text');
+    t.setAttribute('class','citylabel');
+    var above = c.y>110;
+    t.setAttribute('x', c.x + (c.x>820? -6 : 9));
+    t.setAttribute('y', c.y + (above? -8 : 16));
+    if(c.x>820) t.setAttribute('text-anchor','end');
+    t.textContent=c.name;
+    g.appendChild(t);
+  }
+  cityLayer.appendChild(g);
+
+  var show=function(){ showTip(c, core); };
+  var hide=function(){ hideTip(); };
+  g.addEventListener('mouseenter',show);
+  g.addEventListener('mousemove',show);
+  g.addEventListener('mouseleave',hide);
+  g.addEventListener('focus',show);
+  g.addEventListener('blur',hide);
+});
+
+/* ---------------- TOOLTIP ---------------- */
+var tip=$('#tip'), mapshell=$('.mapshell');
+function showTip(c, coreEl){
+  var svg=$('#usmap');
+  var vb=svg.viewBox.baseVal;
+  var rect=svg.getBoundingClientRect();
+  var scaleX=rect.width/vb.width, scaleY=rect.height/vb.height;
+  var px=c.x*scaleX, py=c.y*scaleY;
+  tip.innerHTML='<h4>'+c.name+'</h4>'+
+    '<span class="region-tag" style="color:'+REGION_COLOR[c.region]+'">'+REGION_LABEL[c.region]+'</span>'+
+    '<dl><dt>Venue</dt><dd>'+c.venue+'</dd>'+
+    '<dt>First yr</dt><dd>'+c.year+'</dd>'+
+    '<dt>Shows</dt><dd>~'+c.shows+'</dd></dl>'+
+    '<p style="margin:8px 0 0;font-size:12px;color:#5c4f3c">'+c.note+'</p>'+
+    '<div class="il">Illustrative sample figures</div>';
+  tip.classList.add('show');
+  var tw=tip.offsetWidth, th=tip.offsetHeight;
+  var left=px+16, top=py-th-10;
+  if(left+tw>rect.width-8) left=px-tw-16;
+  if(left<8) left=8;
+  if(top<8) top=py+18;
+  tip.style.left=left+'px'; tip.style.top=top+'px';
+}
+function hideTip(){ tip.classList.remove('show'); }
+
+/* ---------------- RENDER: apply state ---------------- */
+function visible(c){ return (state.region==='all'||c.region===state.region) && c.year<=state.year; }
+
+function applyState(){
+  // dots
+  document.querySelectorAll('.city').forEach(function(g){
+    var c=CITY_BY_ID[g.getAttribute('data-id')];
+    g.classList.toggle('hidden', !visible(c));
+  });
+  // route
+  buildRoute();
+  // list + table
+  renderCards();
+  renderTable();
+  // caption
+  var ty=tourForYear(state.year);
+  $('#tourCaption').innerHTML='Route shown: <strong style="color:var(--gold)">'+ty+' leg</strong>'+(ty!==state.year?' (nearest ≤ '+state.year+')':'')+' — press play to trace it';
+  $('#yearBox').textContent=state.year;
+  $('#yearRange').setAttribute('aria-valuetext', String(state.year));
+  var vis=CITIES.filter(visible).length;
+  $('#listCount').textContent=vis+' cities active by '+state.year+(state.region!=='all'?' · '+REGION_LABEL[state.region]:'');
+}
+
+/* ---------------- CARDS ---------------- */
+function renderCards(){
+  var grid=$('#cardGrid'); grid.innerHTML='';
+  CITIES.filter(visible).sort(function(a,b){return b.shows-a.shows;}).forEach(function(c){
+    var col=REGION_COLOR[c.region];
+    var el=document.createElement('article');
+    el.className='citycard';
+    el.style.setProperty('--west',col);
+    el.innerHTML=
+      '<span class="rtag" style="color:'+col+'">'+REGION_LABEL[c.region]+'</span>'+
+      '<h3>'+c.name+'</h3>'+
+      '<div class="venue">'+c.venue+'</div>'+
+      '<div class="meta">'+
+        '<div class="m"><span class="label" style="font-size:10px">First yr</span><span class="num">'+c.year+'</span></div>'+
+        '<div class="m" style="flex:1"><span class="label" style="font-size:10px">Shows (illus.)</span><span class="num">~'+c.shows+'</span>'+
+          '<div class="bar"><i style="width:'+Math.min(100,c.shows/92*100)+'%;background:'+col+'"></i></div></div>'+
+      '</div>'+
+      '<p class="note">'+c.note+'</p>';
+    el.querySelector('.citycard, &::before');
+    el.style.setProperty('--rc',col);
+    // color the left bar via ::before override
+    el.insertAdjacentHTML('afterbegin','<style>#tmp{}</style>');
+    el.querySelector('style').remove();
+    el.firstChild; 
+    el.setAttribute('style','--west:'+col);
+    grid.appendChild(el);
+  });
+}
+
+/* ---------------- TABLE ---------------- */
+function renderTable(){
+  var tb=$('#tableBody'); tb.innerHTML='';
+  CITIES.filter(visible).sort(function(a,b){return a.name.localeCompare(b.name);}).forEach(function(c){
+    var tr=document.createElement('tr');
+    tr.innerHTML=
+      '<th scope="row"><span class="rd" style="background:'+REGION_COLOR[c.region]+'"></span>'+c.name+'</th>'+
+      '<td>'+REGION_LABEL[c.region]+'</td>'+
+      '<td>'+c.venue+'</td>'+
+      '<td class="num-cell">'+c.year+'</td>'+
+      '<td class="num-cell">~'+c.shows+'</td>'+
+      '<td>'+c.note+'</td>';
+    tb.appendChild(tr);
+  });
+}
+
+/* ---------------- ROUTE PLAYBACK ---------------- */
+var routePath=$('#routePath'), routeGhost=$('#routeGhost'), marker=$('#marker');
+var routeLen=0, playing=false, rafId=null, lastT=0, dist=0;
+var SPEED=170; // px per second along the path
+
+function tourPoints(){
+  var ids=TOURS[tourForYear(state.year)];
+  return ids.map(function(id){ return CITY_BY_ID[id]; }).filter(Boolean);
+}
+function buildRoute(){
+  var pts=tourPoints();
+  if(pts.length<2){ routePath.setAttribute('d',''); routeGhost.setAttribute('d',''); return; }
+  var d='M'+pts[0].x+','+pts[0].y;
+  for(var i=1;i<pts.length;i++) d+=' L'+pts[i].x+','+pts[i].y;
+  routePath.setAttribute('d',d);
+  routeGhost.setAttribute('d',d);
+  routeLen=routePath.getTotalLength();
+  resetRoute();
+}
+function resetRoute(){
+  dist=0;
+  routePath.style.strokeDasharray=routeLen;
+  routePath.style.strokeDashoffset=routeLen;
+  var p0=routePath.getPointAtLength(0);
+  marker.setAttribute('cx',p0.x); marker.setAttribute('cy',p0.y);
+}
+function frame(t){
+  if(!playing) return;
+  if(!lastT) lastT=t;
+  var dt=(t-lastT)/1000; lastT=t;
+  dist += SPEED*dt;
+  if(dist>=routeLen){ dist=routeLen; }
+  routePath.style.strokeDashoffset=(routeLen-dist);
+  var p=routePath.getPointAtLength(dist);
+  marker.setAttribute('cx',p.x); marker.setAttribute('cy',p.y);
+  if(dist>=routeLen){ pause(); return; }
+  rafId=requestAnimationFrame(frame);
+}
+function play(){
+  if(routeLen<=0) return;
+  if(dist>=routeLen) resetRoute();
+  playing=true; lastT=0;
+  $('#playIcon').textContent='❚❚'; $('#playTxt').textContent='Pause'; $('#playBtn').setAttribute('aria-pressed','true');
+  rafId=requestAnimationFrame(frame);
+}
+function pause(){
+  playing=false; if(rafId) cancelAnimationFrame(rafId);
+  $('#playIcon').textContent='▶'; $('#playTxt').textContent='Play route'; $('#playBtn').setAttribute('aria-pressed','false');
+}
+$('#playBtn').addEventListener('click',function(){ playing?pause():play(); });
+
+/* ---------------- CONTROLS WIRING ---------------- */
+$('#yearRange').addEventListener('input',function(e){
+  state.year=parseInt(e.target.value,10);
+  pause();
+  applyState();
+});
+$('#regionChips').addEventListener('click',function(e){
+  var b=e.target.closest('.chip'); if(!b) return;
+  state.region=b.getAttribute('data-region');
+  Array.prototype.forEach.call(this.querySelectorAll('.chip'),function(c){ c.setAttribute('aria-pressed', c===b?'true':'false'); });
+  applyState();
+});
+document.querySelector('.viewtabs').addEventListener('click',function(e){
+  var b=e.target.closest('button'); if(!b) return;
+  state.view=b.getAttribute('data-view');
+  Array.prototype.forEach.call(this.querySelectorAll('button'),function(x){ x.setAttribute('aria-pressed', x===b?'true':'false'); });
+  ['map','list','table'].forEach(function(v){ $('#view-'+v).classList.toggle('on', v===state.view); });
+});
+
+/* ---------------- RIBBON CHART ---------------- */
+(function buildRibbons(){
+  var svg=$('#ribbonSvg');
+  var W=1000,H=430, padL=200, padR=30, top=34, rowH=36;
+  var yA=1965, yB=1996, innerW=W-padL-padR;
+  function xOf(y){ return padL + (y-yA)/(yB-yA)*innerW; }
+  // axis grid
+  for(var yr=1965; yr<=1995; yr+=5){
+    var gx=xOf(yr);
+    var ln=document.createElementNS(SVGNS,'line');
+    ln.setAttribute('x1',gx); ln.setAttribute('x2',gx); ln.setAttribute('y1',top-8); ln.setAttribute('y2', top+VENUES.length*rowH+4);
+    ln.setAttribute('stroke','rgba(244,236,224,.10)'); svg.appendChild(ln);
+    var tx=document.createElementNS(SVGNS,'text');
+    tx.setAttribute('class','axis'); tx.setAttribute('x',gx); tx.setAttribute('y',top-14); tx.setAttribute('text-anchor','middle');
+    tx.textContent="’"+String(yr).slice(2); svg.appendChild(tx);
+  }
+  // defs gradient
+  var defs=document.createElementNS(SVGNS,'defs');
+  defs.innerHTML='<linearGradient id="ribG" x1="0" x2="1" y1="0" y2="0">'+
+    '<stop offset="0" stop-color="#9b5de5" stop-opacity=".95"/>'+
+    '<stop offset=".5" stop-color="#3ac6d6" stop-opacity=".95"/>'+
+    '<stop offset="1" stop-color="#ffca3a" stop-opacity=".95"/></linearGradient>';
+  svg.appendChild(defs);
+
+  VENUES.forEach(function(vn,i){
+    var y=top + i*rowH + rowH/2;
+    var x1=xOf(vn.s), x2=xOf(vn.e);
+    var h= 7 + vn.nights/62*22;
+    var col=REGION_COLOR[vn.region];
+    // ribbon body
+    var rect=document.createElementNS(SVGNS,'rect');
+    rect.setAttribute('class','rib');
+    rect.setAttribute('x',x1); rect.setAttribute('y', y-h/2);
+    rect.setAttribute('width', Math.max(6,x2-x1)); rect.setAttribute('height',h);
+    rect.setAttribute('rx', h/2);
+    rect.setAttribute('fill', col); rect.setAttribute('opacity','.82');
+    var tt=document.createElementNS(SVGNS,'title');
+    tt.textContent=vn.v+' — '+vn.city+' · '+vn.s+'–'+vn.e+' · ~'+vn.nights+' nights (illustrative)';
+    rect.appendChild(tt);
+    svg.appendChild(rect);
+    // spectral flash cap at end
+    var cap=document.createElementNS(SVGNS,'circle');
+    cap.setAttribute('cx',x2); cap.setAttribute('cy',y); cap.setAttribute('r', Math.max(3,h/2));
+    cap.setAttribute('fill','url(#ribG)'); cap.setAttribute('opacity','.9'); svg.appendChild(cap);
+    // labels
+    var vl=document.createElementNS(SVGNS,'text');
+    vl.setAttribute('class','vlabel'); vl.setAttribute('x',padL-16); vl.setAttribute('y',y-2); vl.setAttribute('text-anchor','end');
+    vl.textContent=vn.v; svg.appendChild(vl);
+    var vc=document.createElementNS(SVGNS,'text');
+    vc.setAttribute('class','vcity'); vc.setAttribute('x',padL-16); vc.setAttribute('y',y+11); vc.setAttribute('text-anchor','end');
+    vc.textContent=vn.city; svg.appendChild(vc);
+  });
+})();
+
+/* ---------------- SEASONAL HEAT-BAND ---------------- */
+(function buildSeasonal(){
+  var svg=$('#seasonalSvg');
+  var W=1000,H=340, padL=150, padT=44, cellW=(W-padL-30)/12, rowH=44;
+  var regions=['West','Mountain','Midwest','South','Northeast'];
+  // month labels
+  MONTHS.forEach(function(m,i){
+    var tx=document.createElementNS(SVGNS,'text');
+    tx.setAttribute('class','mo'); tx.setAttribute('x', padL+i*cellW+cellW/2); tx.setAttribute('y', padT-14);
+    tx.setAttribute('text-anchor','middle'); tx.textContent=m; svg.appendChild(tx);
+  });
+  var seasonNames=[['Winter',0,2],['Spring',2,5],['Summer',5,8],['Autumn',8,11]];
+  seasonNames.forEach(function(s){
+    var x=padL+s[1]*cellW; var w=(s[2]-s[1])*cellW;
+    var tx=document.createElementNS(SVGNS,'text');
+    tx.setAttribute('class','axis'); tx.setAttribute('x', x+w/2); tx.setAttribute('y', padT-30);
+    tx.setAttribute('text-anchor','middle'); tx.textContent=s[0]; svg.appendChild(tx);
+  });
+  regions.forEach(function(rg,ri){
+    var y=padT + ri*rowH;
+    var col=REGION_COLOR[rg];
+    // row label
+    var lab=document.createElementNS(SVGNS,'text');
+    lab.setAttribute('class','rlab'); lab.setAttribute('x',padL-16); lab.setAttribute('y', y+rowH/2+4); lab.setAttribute('text-anchor','end');
+    lab.textContent=REGION_LABEL[rg]; svg.appendChild(lab);
+    var dot=document.createElementNS(SVGNS,'circle');
+    dot.setAttribute('cx',padL-8); dot.setAttribute('cy',y+rowH/2); dot.setAttribute('r',4); dot.setAttribute('fill',col); svg.appendChild(dot);
+    SEASON[rg].forEach(function(val,mi){
+      var x=padL+mi*cellW;
+      var cell=document.createElementNS(SVGNS,'rect');
+      cell.setAttribute('x',x+2); cell.setAttribute('y',y+3);
+      cell.setAttribute('width',cellW-4); cell.setAttribute('height',rowH-6);
+      cell.setAttribute('rx',5);
+      cell.setAttribute('fill',col); cell.setAttribute('opacity', 0.12+val*0.8);
+      var tt=document.createElementNS(SVGNS,'title');
+      tt.textContent=REGION_LABEL[rg]+' · '+MONTHS[mi]+' · emphasis '+Math.round(val*100)+'% (illustrative)';
+      cell.appendChild(tt);
+      svg.appendChild(cell);
+    });
+  });
+  // caption
+  var cap=document.createElementNS(SVGNS,'text');
+  cap.setAttribute('class','mo'); cap.setAttribute('x',padL); cap.setAttribute('y', padT+5*rowH+22);
+  cap.textContent='Darker = greater illustrative seasonal emphasis'; svg.appendChild(cap);
+})();
+
+/* ---------------- INIT ---------------- */
+// keyboard: space toggles play when focus not on a control
+document.addEventListener('keydown',function(e){
+  if(e.code==='Space' && state.view==='map' && document.activeElement && document.activeElement.tagName!=='INPUT' && document.activeElement.tagName!=='BUTTON'){
+    e.preventDefault(); playing?pause():play();
+  }
+});
+applyState();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/builds/wall-of-sound/cover.png b/builds/wall-of-sound/cover.png
new file mode 100644
index 0000000..07be94f
Binary files /dev/null and b/builds/wall-of-sound/cover.png differ
diff --git a/builds/wall-of-sound/index.html b/builds/wall-of-sound/index.html
new file mode 100644
index 0000000..c879d1f
--- /dev/null
+++ b/builds/wall-of-sound/index.html
@@ -0,0 +1,796 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Wall of Sound — An Immersive Room</title>
+<style>
+  :root{
+    --bg:#0d0d0f; --surface:#17171c; --surface-2:#1d1d24;
+    --text:#f5f2ea; --muted:#8a8578;
+    --gold:#c9a961; --bronze:#7d6a45;
+    --line:rgba(201,169,97,.18);
+    /* perspective-driven ambient accents (default: stage) */
+    --amb1:#c9a961; --amb2:#7d6a45; --amb-glow:rgba(201,169,97,.18);
+    --field:#c9a961;
+    --pad:clamp(20px,5vw,64px);
+    --ease:cubic-bezier(.22,1,.36,1);
+  }
+  *{box-sizing:border-box}
+  html{scroll-behavior:smooth}
+  body{
+    margin:0; background:var(--bg); color:var(--text);
+    font-family:-apple-system,'Segoe UI','Helvetica Neue',Arial,sans-serif;
+    font-size:16px; line-height:1.65; letter-spacing:.01em;
+    overflow-x:hidden;
+    -webkit-font-smoothing:antialiased;
+  }
+  h1,h2,h3,.display{font-family:'Didot','Bodoni MT','Playfair Display',Georgia,serif;font-weight:400}
+  ::selection{background:var(--gold);color:#0d0d0f}
+
+  /* ambient drifting backdrop */
+  .ambient{
+    position:fixed;inset:0;z-index:0;pointer-events:none;
+    background:
+      radial-gradient(60vw 60vw at 22% 18%, var(--amb-glow), transparent 60%),
+      radial-gradient(50vw 50vw at 82% 78%, rgba(125,106,69,.14), transparent 62%),
+      radial-gradient(40vw 40vw at 50% 50%, rgba(201,169,97,.05), transparent 70%);
+    transition:background 1.4s var(--ease);
+    animation:drift 18s ease-in-out infinite alternate;
+  }
+  @keyframes drift{
+    from{transform:translate3d(-1.5%,-1%,0) scale(1.02)}
+    to{transform:translate3d(1.5%,1.5%,0) scale(1.06)}
+  }
+  body.quiet .ambient{animation:none}
+
+  .wrap{position:relative;z-index:1;max-width:1200px;margin:0 auto;padding:0 var(--pad)}
+
+  /* ---- utility ---- */
+  .kicker{
+    font-size:12px;letter-spacing:.15em;text-transform:uppercase;color:var(--muted);
+    display:block;margin-bottom:12px;
+  }
+  .lead{font-size:clamp(17px,2.1vw,21px);color:var(--muted);max-width:62ch}
+  .rule{height:1px;background:var(--line);border:0;margin:0}
+
+  a.link{color:var(--gold);text-decoration:none;border-bottom:1px solid var(--line)}
+
+  /* entrance */
+  .fade{opacity:0;transform:translateY(28px)}
+  .fade.in{animation:fadeUp .7s var(--ease) both}
+  @keyframes fadeUp{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}
+  body.quiet .fade{opacity:1!important;transform:none!important;animation:none!important}
+
+  /* ================= TOP BAR ================= */
+  header.bar{
+    position:sticky;top:0;z-index:40;
+    display:flex;align-items:center;justify-content:space-between;gap:16px;
+    padding:14px var(--pad);
+    background:rgba(13,13,15,.72);backdrop-filter:blur(14px);
+    border-bottom:1px solid var(--line);
+  }
+  .brand{display:flex;align-items:baseline;gap:12px;min-width:0}
+  .brand .mark{font-family:'Didot',Georgia,serif;font-size:20px;letter-spacing:.02em;white-space:nowrap}
+  .brand .sub{font-size:12px;letter-spacing:.15em;text-transform:uppercase;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+  .bar-actions{display:flex;gap:8px;align-items:center;flex-shrink:0}
+  .btn{
+    font:inherit;font-size:12px;letter-spacing:.12em;text-transform:uppercase;
+    color:var(--text);background:transparent;border:1px solid var(--line);
+    padding:9px 14px;border-radius:10px;cursor:pointer;
+    transition:all .2s var(--ease);white-space:nowrap;
+  }
+  .btn:hover,.btn:focus-visible{border-color:var(--gold);color:var(--gold);outline:none}
+  .btn[aria-pressed="true"]{background:var(--gold);color:#0d0d0f;border-color:var(--gold)}
+  :focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:6px}
+
+  /* ================= HERO / STAGE ================= */
+  .hero{padding:clamp(48px,9vw,96px) 0 40px;text-align:center}
+  .hero h1{
+    font-size:clamp(38px,8vw,67px);line-height:1.02;margin:0 auto 18px;
+    max-width:16ch;letter-spacing:.01em;
+  }
+  .hero h1 em{font-style:italic;color:var(--gold)}
+  .hero .lead{margin:0 auto 8px;text-align:center}
+
+  /* installation frame */
+  .installation{
+    margin:44px auto 0;max-width:920px;position:relative;
+    aspect-ratio:16/10;border:1px solid var(--line);border-radius:16px;
+    background:
+      radial-gradient(120% 120% at 50% 120%, rgba(201,169,97,.08), transparent 55%),
+      linear-gradient(180deg,#101014,#0a0a0c);
+    box-shadow:0 30px 90px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.03);
+    overflow:hidden;
+  }
+  .installation svg{position:absolute;inset:0;width:100%;height:100%}
+  .stage-label{
+    position:absolute;left:16px;bottom:14px;
+    font-size:12px;letter-spacing:.15em;text-transform:uppercase;color:var(--muted);
+    z-index:5;
+  }
+  .stage-label b{color:var(--gold);font-weight:400}
+
+  /* animated waveform + fields controlled by JS/CSS classes */
+  .field-ring{transition:opacity 1s var(--ease), stroke 1.2s var(--ease)}
+  .pulse{transform-origin:center;transform-box:fill-box}
+  body:not(.quiet) .pulse.a{animation:ripple 5.5s var(--ease) infinite}
+  body:not(.quiet) .pulse.b{animation:ripple 5.5s var(--ease) infinite .9s}
+  body:not(.quiet) .pulse.c{animation:ripple 5.5s var(--ease) infinite 1.8s}
+  body:not(.quiet) .pulse.d{animation:ripple 5.5s var(--ease) infinite 2.7s}
+  @keyframes ripple{
+    0%{opacity:.85;transform:scale(.28)}
+    80%{opacity:0;transform:scale(1.9)}
+    100%{opacity:0;transform:scale(1.9)}
+  }
+  .wave path{transition:opacity .6s var(--ease)}
+  body:not(.quiet).demo-on .wave path{animation:wavemove 3s linear infinite}
+  @keyframes wavemove{to{transform:translateX(-160px)}}
+  .signal{stroke-dasharray:6 10;transition:stroke 1s var(--ease)}
+  body:not(.quiet).demo-on .signal{animation:dash 2.2s linear infinite}
+  @keyframes dash{to{stroke-dashoffset:-160}}
+
+  .speaker-cell{transition:fill 1.1s var(--ease), opacity 1.1s var(--ease)}
+  .listener{transition:transform 1.1s var(--ease), opacity 1s var(--ease)}
+  body:not(.quiet).demo-on .lit-meter rect{animation:meter 1.4s var(--ease) infinite alternate}
+  @keyframes meter{from{transform:scaleY(.35)}to{transform:scaleY(1)}}
+  .lit-meter rect{transform-origin:bottom;transform-box:fill-box}
+
+  /* ================= PERSPECTIVE SWITCH ================= */
+  section{padding:clamp(56px,10vw,96px) 0;position:relative}
+  section > .wrap { position:relative }
+  h2.sec{font-size:clamp(28px,5vw,38px);margin:0 0 8px;letter-spacing:.01em}
+
+  .persp-tabs{display:flex;flex-wrap:wrap;gap:10px;margin:26px 0 34px}
+  .tab{
+    flex:1 1 180px;text-align:left;
+    background:var(--surface);border:1px solid var(--line);border-radius:14px;
+    padding:16px 18px;cursor:pointer;transition:all .25s var(--ease);
+    color:var(--text);font:inherit;position:relative;overflow:hidden;
+  }
+  .tab .n{font-size:12px;letter-spacing:.15em;text-transform:uppercase;color:var(--muted)}
+  .tab .t{font-family:'Didot',Georgia,serif;font-size:22px;margin-top:6px}
+  .tab::after{content:"";position:absolute;left:0;bottom:0;height:3px;width:0;background:var(--field);transition:width .35s var(--ease)}
+  .tab:hover{transform:translateY(-3px);box-shadow:0 16px 48px rgba(0,0,0,.28)}
+  .tab[aria-selected="true"]{border-color:var(--field);background:var(--surface-2)}
+  .tab[aria-selected="true"]::after{width:100%}
+  .tab[aria-selected="true"] .t{color:var(--field)}
+
+  .persp-body{
+    display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(24px,4vw,48px);align-items:center;
+  }
+  .diagram{
+    border:1px solid var(--line);border-radius:16px;overflow:hidden;position:relative;
+    aspect-ratio:4/3;background:linear-gradient(180deg,#101014,#0b0b0e);
+    box-shadow:0 20px 60px rgba(0,0,0,.4);
+  }
+  .diagram svg{width:100%;height:100%;display:block}
+  .persp-text .kicker{color:var(--field)}
+  .persp-text h3{font-size:clamp(24px,4vw,32px);margin:0 0 14px}
+  .persp-text p{color:var(--muted);margin:0 0 18px}
+  .meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:8px}
+  .meta{border-top:1px solid var(--line);padding-top:10px}
+  .meta .k{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
+  .meta .v{font-family:'Didot',Georgia,serif;font-size:19px;color:var(--text);margin-top:2px}
+
+  /* ================= EXPLODED VIEW ================= */
+  .exploded-stage{
+    border:1px solid var(--line);border-radius:16px;overflow:hidden;
+    background:linear-gradient(180deg,#101014,#0a0a0c);position:relative;
+    box-shadow:0 24px 70px rgba(0,0,0,.45);
+  }
+  .exploded-stage svg{width:100%;height:auto;display:block}
+  .layer{transition:transform .8s var(--ease), opacity .6s var(--ease)}
+  .exploded .layer.l1{transform:translateY(-46px)}
+  .exploded .layer.l2{transform:translateY(-16px)}
+  .exploded .layer.l3{transform:translateY(16px)}
+  .exploded .layer.l4{transform:translateY(46px)}
+  .callout{opacity:0;transition:opacity .5s var(--ease) .2s}
+  .exploded .callout{opacity:1}
+
+  /* ================= CARDS GRID ================= */
+  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:24px;margin-top:36px}
+  .card{
+    background:var(--surface);border:1px solid var(--line);border-radius:14px;
+    padding:24px;transition:transform .25s var(--ease),box-shadow .25s var(--ease),border-color .25s var(--ease);
+    position:relative;
+  }
+  .card:hover{transform:translateY(-4px);box-shadow:0 16px 48px rgba(0,0,0,.28);border-color:var(--gold)}
+  .card .spec{font-size:12px;letter-spacing:.15em;text-transform:uppercase;color:var(--gold);margin-bottom:6px}
+  .card h3{font-size:23px;margin:0 0 10px}
+  .card p{color:var(--muted);font-size:15px;margin:0 0 14px}
+  .card .stats{display:flex;flex-wrap:wrap;gap:8px}
+  .chip{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);border:1px solid var(--line);border-radius:20px;padding:5px 11px}
+  .card-icon{width:44px;height:44px;margin-bottom:16px;color:var(--gold)}
+
+  /* history — timeline */
+  .timeline{margin-top:36px;border-left:1px solid var(--line);padding-left:28px;display:grid;gap:34px}
+  .tl{position:relative}
+  .tl::before{content:"";position:absolute;left:-34px;top:6px;width:11px;height:11px;border-radius:50%;background:var(--gold);box-shadow:0 0 0 5px rgba(201,169,97,.12)}
+  .tl .yr{font-family:'Didot',Georgia,serif;font-size:26px;color:var(--gold)}
+  .tl h3{font-size:20px;margin:2px 0 8px}
+  .tl p{color:var(--muted);margin:0;max-width:60ch}
+
+  /* glossary */
+  details.gl{
+    border-bottom:1px solid var(--line);padding:4px 0;
+  }
+  details.gl summary{
+    list-style:none;cursor:pointer;padding:18px 4px;display:flex;justify-content:space-between;align-items:baseline;gap:16px;
+    transition:color .2s var(--ease);
+  }
+  details.gl summary::-webkit-details-marker{display:none}
+  details.gl summary .term{font-family:'Didot',Georgia,serif;font-size:22px}
+  details.gl summary .pron{font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
+  details.gl[open] summary .term{color:var(--gold)}
+  details.gl .def{padding:0 4px 20px;color:var(--muted);max-width:70ch;animation:fadeUp .4s var(--ease) both}
+  .plus{color:var(--gold);font-size:20px;transition:transform .3s var(--ease)}
+  details.gl[open] .plus{transform:rotate(45deg)}
+
+  footer{padding:64px var(--pad) 80px;border-top:1px solid var(--line);text-align:center;position:relative;z-index:1}
+  footer .disc{max-width:64ch;margin:16px auto 0;font-size:13px;color:var(--muted)}
+
+  .demo-note{
+    display:inline-flex;align-items:center;gap:10px;margin-top:22px;
+    font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
+    border:1px solid var(--line);border-radius:20px;padding:8px 16px;
+  }
+  .dot{width:8px;height:8px;border-radius:50%;background:var(--muted);transition:background .3s}
+  .demo-on .dot{background:var(--gold);box-shadow:0 0 12px var(--gold)}
+
+  .quiet-flag{
+    position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(140%);
+    background:var(--surface-2);border:1px solid var(--gold);color:var(--gold);
+    padding:10px 20px;border-radius:24px;z-index:60;
+    font-size:12px;letter-spacing:.14em;text-transform:uppercase;
+    transition:transform .5s var(--ease);box-shadow:0 10px 40px rgba(0,0,0,.5);
+  }
+  body.quiet .quiet-flag{transform:translateX(-50%) translateY(0)}
+
+  .kbd{font-family:ui-monospace,Menlo,monospace;font-size:12px;background:var(--surface-2);border:1px solid var(--line);border-bottom-width:2px;border-radius:6px;padding:2px 7px;color:var(--text)}
+
+  @media(max-width:760px){
+    .persp-body{grid-template-columns:1fr}
+    .brand .sub{display:none}
+    .meta-grid{grid-template-columns:1fr}
+  }
+  @media(prefers-reduced-motion:reduce){
+    *{animation:none!important;scroll-behavior:auto}
+    .fade{opacity:1;transform:none}
+  }
+</style>
+</head>
+<body>
+<div class="ambient" aria-hidden="true"></div>
+
+<header class="bar">
+  <div class="brand">
+    <span class="mark">Wall of Sound</span>
+    <span class="sub">An Immersive Room · Gallery Installation</span>
+  </div>
+  <div class="bar-actions">
+    <button class="btn" id="demoBtn" aria-pressed="false" title="Toggle the honest visual simulation (no audio)">◈ Demo Signal</button>
+    <button class="btn" id="quietBtn" aria-pressed="false" title="Stop all motion; preserve the design">Quiet Mode</button>
+  </div>
+</header>
+
+<main>
+  <!-- HERO -->
+  <section class="hero" id="top">
+    <div class="wrap">
+      <span class="kicker fade">A Virtual Installation · Sound as Architecture</span>
+      <h1 class="fade">Standing Inside the <em>Wall of Sound</em></h1>
+      <p class="lead fade">In 1974 a band tried to remove distortion from live music by building a speaker system that was, in effect, a building. This room is a study of that ambition — a place to move between vantage points and watch how a single signal reshapes the air around you.</p>
+
+      <div class="installation fade" role="img" aria-label="Abstract monumental speaker array with concentric sound fields and a moving waveform.">
+        <svg viewBox="0 0 920 575" preserveAspectRatio="xMidYMid slice" id="heroSvg">
+          <defs>
+            <linearGradient id="col" x1="0" y1="0" x2="0" y2="1">
+              <stop offset="0" stop-color="#22222a"/><stop offset="1" stop-color="#0c0c10"/>
+            </linearGradient>
+            <radialGradient id="pool" cx="50%" cy="82%" r="60%">
+              <stop offset="0" stop-color="var(--amb1)" stop-opacity=".28"/>
+              <stop offset="1" stop-color="var(--amb1)" stop-opacity="0"/>
+            </radialGradient>
+            <filter id="soft"><feGaussianBlur stdDeviation="2.2"/></filter>
+          </defs>
+
+          <!-- pool of light -->
+          <rect x="0" y="0" width="920" height="575" fill="url(#pool)"/>
+
+          <!-- concentric sound fields -->
+          <g fill="none" stroke="var(--field)" stroke-opacity=".22">
+            <circle class="field-ring" cx="460" cy="250" r="120"/>
+            <circle class="field-ring" cx="460" cy="250" r="185"/>
+            <circle class="field-ring" cx="460" cy="250" r="255"/>
+            <circle class="field-ring" cx="460" cy="250" r="330"/>
+          </g>
+          <!-- animated pulses -->
+          <g fill="none" stroke="var(--field)" stroke-width="1.4" stroke-opacity=".5">
+            <circle class="pulse a" cx="460" cy="250" r="120"/>
+            <circle class="pulse b" cx="460" cy="250" r="120"/>
+            <circle class="pulse c" cx="460" cy="250" r="120"/>
+            <circle class="pulse d" cx="460" cy="250" r="120"/>
+          </g>
+
+          <!-- THE ARRAY : abstract towers of speaker cells -->
+          <g id="array">
+            <!-- back scaffold -->
+            <g stroke="#3a3527" stroke-width="1.5" stroke-opacity=".7">
+              <line x1="300" y1="70" x2="300" y2="470"/><line x1="620" y1="70" x2="620" y2="470"/>
+              <line x1="300" y1="70" x2="620" y2="70"/><line x1="300" y1="470" x2="620" y2="470"/>
+              <line x1="360" y1="70" x2="360" y2="470"/><line x1="460" y1="70" x2="460" y2="470"/><line x1="560" y1="70" x2="560" y2="470"/>
+            </g>
+          </g>
+
+          <!-- moving waveform band -->
+          <g class="wave" opacity=".9">
+            <path d="M-160,250 q40,-40 80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0 t80,0"
+                  fill="none" stroke="var(--field)" stroke-width="1.6" stroke-opacity=".5"/>
+          </g>
+
+          <!-- signal path from board to array -->
+          <path class="signal" d="M120,520 C260,520 300,360 460,320" fill="none" stroke="var(--amb1)" stroke-width="1.6" stroke-opacity=".8"/>
+          <circle cx="120" cy="520" r="5" fill="var(--amb1)"/>
+
+          <!-- level meter -->
+          <g class="lit-meter" transform="translate(720,360)" opacity=".85">
+            <rect x="0" width="8" height="70" fill="var(--field)" fill-opacity=".7"/>
+            <rect x="14" width="8" height="70" fill="var(--field)" fill-opacity=".55"/>
+            <rect x="28" width="8" height="70" fill="var(--field)" fill-opacity=".7"/>
+            <rect x="42" width="8" height="70" fill="var(--field)" fill-opacity=".5"/>
+          </g>
+        </svg>
+        <div class="stage-label">Perspective: <b id="stageName">Front Rail</b> · <span id="demoState">signal idle</span></div>
+      </div>
+
+      <div class="demo-note"><span class="dot"></span><span id="noteTxt">Visual simulation — this installation makes no sound. Toggle “Demo Signal” to animate the modeled signal path.</span></div>
+    </div>
+  </section>
+
+  <hr class="rule"/>
+
+  <!-- PERSPECTIVES -->
+  <section id="perspectives">
+    <div class="wrap">
+      <span class="kicker fade" style="color:var(--field)">Four Listening Perspectives</span>
+      <h2 class="sec fade">Where you stand is what you hear</h2>
+      <p class="lead fade">A wall of sound is not one experience but a gradient of them. Choose a vantage point — the diagram, the ambient light, the interpretive text, and the modeled signal path all change with you.</p>
+
+      <div class="persp-tabs fade" role="tablist" aria-label="Listening perspectives">
+        <button class="tab" role="tab" aria-selected="false" data-p="stage" id="tab-stage">
+          <span class="n">01 · On</span><span class="t">Stage</span>
+        </button>
+        <button class="tab" role="tab" aria-selected="true" data-p="rail" id="tab-rail">
+          <span class="n">02 · The</span><span class="t">Front Rail</span>
+        </button>
+        <button class="tab" role="tab" aria-selected="false" data-p="board" id="tab-board">
+          <span class="n">03 · At the</span><span class="t">Soundboard</span>
+        </button>
+        <button class="tab" role="tab" aria-selected="false" data-p="hill" id="tab-hill">
+          <span class="n">04 · The</span><span class="t">Hillside</span>
+        </button>
+      </div>
+
+      <div class="persp-body">
+        <div class="diagram fade" id="diagram" role="img" aria-live="polite" aria-label="Spatial diagram of the chosen perspective.">
+          <svg viewBox="0 0 480 360" id="planSvg">
+            <defs>
+              <radialGradient id="d-pool" cx="50%" cy="30%" r="70%">
+                <stop offset="0" stop-color="var(--field)" stop-opacity=".22"/>
+                <stop offset="1" stop-color="var(--field)" stop-opacity="0"/>
+              </radialGradient>
+            </defs>
+            <rect x="0" y="0" width="480" height="360" fill="url(#d-pool)"/>
+            <!-- the wall (fixed) -->
+            <g id="wall">
+              <rect x="150" y="26" width="180" height="40" rx="4" fill="none" stroke="var(--field)" stroke-opacity=".8"/>
+              <g stroke="var(--field)" stroke-opacity=".4">
+                <line x1="180" y1="26" x2="180" y2="66"/><line x1="210" y1="26" x2="210" y2="66"/>
+                <line x1="240" y1="26" x2="240" y2="66"/><line x1="270" y1="26" x2="270" y2="66"/><line x1="300" y1="26" x2="300" y2="66"/>
+              </g>
+              <text x="240" y="18" text-anchor="middle" fill="var(--muted)" font-size="10" letter-spacing="2">THE ARRAY</text>
+            </g>
+            <!-- radiating field -->
+            <g id="dfield" fill="none" stroke="var(--field)" stroke-opacity=".3">
+              <path d="M240,66 Q140,150 120,300"/>
+              <path d="M240,66 Q240,180 240,320"/>
+              <path d="M240,66 Q340,150 360,300"/>
+            </g>
+            <!-- listener marker (moves per perspective) -->
+            <g class="listener" id="listener">
+              <circle cx="240" cy="250" r="9" fill="var(--field)"/>
+              <circle cx="240" cy="250" r="18" fill="none" stroke="var(--field)" stroke-opacity=".5"/>
+              <text x="240" y="285" text-anchor="middle" fill="var(--text)" font-size="10" letter-spacing="2" id="youTxt">YOU</text>
+            </g>
+            <!-- signal path (animated when demo on) -->
+            <path class="signal" id="dsignal" d="M240,320 C240,200 240,140 240,66" fill="none" stroke="var(--amb1)" stroke-width="1.4"/>
+          </svg>
+        </div>
+
+        <div class="persp-text fade">
+          <span class="kicker" id="pKicker">Perspective 02</span>
+          <h3 id="pTitle">The Front Rail</h3>
+          <p id="pBody"></p>
+          <div class="meta-grid">
+            <div class="meta"><div class="k">Spatial Character</div><div class="v" id="mChar"></div></div>
+            <div class="meta"><div class="k">Ambient Field</div><div class="v" id="mColor"></div></div>
+            <div class="meta"><div class="k">Distance to Array</div><div class="v" id="mDist"></div></div>
+            <div class="meta"><div class="k">Perceived Image</div><div class="v" id="mImage"></div></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+
+  <hr class="rule"/>
+
+  <!-- EXPLODED VIEW -->
+  <section id="exploded">
+    <div class="wrap">
+      <span class="kicker fade">Technical View · 1970s Engineering Drawing</span>
+      <h2 class="sec fade">The array, taken apart</h2>
+      <p class="lead fade">A monumental column separated into its functional strata. Toggle the exploded view to see how frequency was divided across dedicated banks — the design principle that let each driver do only one job.</p>
+      <p class="fade" style="margin:22px 0"><button class="btn" id="explodeBtn" aria-pressed="false">⤢ Explode the Column</button></p>
+
+      <div class="exploded-stage fade" id="explodeStage">
+        <svg viewBox="0 0 900 460" role="img" aria-label="Exploded technical diagram of the speaker column, four layers.">
+          <defs>
+            <pattern id="grid" width="26" height="26" patternUnits="userSpaceOnUse">
+              <path d="M26 0H0V26" fill="none" stroke="#26221a" stroke-width=".6"/>
+            </pattern>
+          </defs>
+          <rect x="0" y="0" width="900" height="460" fill="url(#grid)"/>
+          <!-- center axis -->
+          <line x1="450" y1="20" x2="450" y2="440" stroke="var(--bronze)" stroke-dasharray="3 6" stroke-opacity=".5"/>
+
+          <!-- Layer 4: sub-bass base -->
+          <g class="layer l4">
+            <rect x="330" y="330" width="240" height="70" rx="5" fill="#15150f" stroke="var(--gold)" stroke-opacity=".7"/>
+            <circle cx="390" cy="365" r="22" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <circle cx="450" cy="365" r="22" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <circle cx="510" cy="365" r="22" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <g class="callout"><line x1="570" y1="365" x2="700" y2="365" stroke="var(--muted)"/><text x="706" y="362" fill="var(--muted)" font-size="12">Sub-bass bank · below 100 Hz</text><text x="706" y="378" fill="var(--muted)" font-size="10" letter-spacing="1">STRAIGHT-LINE, NO CROSSOVER</text></g>
+          </g>
+          <!-- Layer 3: low-mid -->
+          <g class="layer l3">
+            <rect x="355" y="250" width="190" height="66" rx="5" fill="#161610" stroke="var(--gold)" stroke-opacity=".7"/>
+            <circle cx="410" cy="283" r="18" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <circle cx="450" cy="283" r="18" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <circle cx="490" cy="283" r="18" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <g class="callout"><line x1="545" y1="283" x2="700" y2="283" stroke="var(--muted)"/><text x="706" y="280" fill="var(--muted)" font-size="12">Low-mid drivers · 100–400 Hz</text></g>
+          </g>
+          <!-- Layer 2: mid -->
+          <g class="layer l2">
+            <rect x="375" y="180" width="150" height="56" rx="5" fill="#17170f" stroke="var(--gold)" stroke-opacity=".7"/>
+            <rect x="395" y="196" width="26" height="26" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <rect x="437" y="196" width="26" height="26" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <rect x="479" y="196" width="26" height="26" fill="none" stroke="var(--gold)" stroke-opacity=".7"/>
+            <g class="callout"><line x1="375" y1="208" x2="200" y2="208" stroke="var(--muted)"/><text x="196" y="205" text-anchor="end" fill="var(--muted)" font-size="12">Midrange cluster · 400 Hz–4 kHz</text></g>
+          </g>
+          <!-- Layer 1: tweeter crown -->
+          <g class="layer l1">
+            <rect x="398" y="120" width="104" height="44" rx="5" fill="#18180f" stroke="var(--gold)" stroke-opacity=".8"/>
+            <polygon points="420,142 434,132 434,152" fill="none" stroke="var(--gold)" stroke-opacity=".8"/>
+            <polygon points="452,142 466,132 466,152" fill="none" stroke="var(--gold)" stroke-opacity=".8"/>
+            <g class="callout"><line x1="398" y1="142" x2="200" y2="142" stroke="var(--muted)"/><text x="196" y="139" text-anchor="end" fill="var(--muted)" font-size="12">Tweeter crown · above 4 kHz</text></g>
+          </g>
+
+          <text x="450" y="440" text-anchor="middle" fill="var(--muted)" font-size="10" letter-spacing="3">FIG. 1 — VERTICAL DRIVER STRATIFICATION · NOT TO SCALE</text>
+        </svg>
+      </div>
+    </div>
+  </section>
+
+  <hr class="rule"/>
+
+  <!-- HISTORY -->
+  <section id="history">
+    <div class="wrap">
+      <span class="kicker fade">A Concise History</span>
+      <h2 class="sec fade">The most ambitious PA ever built</h2>
+      <p class="lead fade">Between 1973 and 1974 a touring band and its audio engineers pursued a radical idea: instead of amplifying a signal and pointing it at a crowd, build a system so transparent that the amplifier disappears and only the instrument remains.</p>
+
+      <div class="timeline">
+        <div class="tl fade"><div class="yr">1965–72</div><h3>The distortion problem</h3><p>Early rock PA systems muddied everything — vocals fought guitars through the same overtaxed columns, and musicians on stage couldn't hear themselves clearly. The dream took shape: a system with no compromise between fidelity and volume.</p></div>
+        <div class="tl fade"><div class="yr">1973</div><h3>One driver, one job</h3><p>Engineers split the signal so each range of frequencies had its own dedicated bank of speakers. Instruments were kept on separate channels rather than mixed into a common muddy bus, letting each voice radiate cleanly from its own column.</p></div>
+        <div class="tl fade"><div class="yr">1974</div><h3>The wall stands</h3><p>At full height the system rose behind the musicians as a towering array of hundreds of drivers — a monument of speaker cabinets that doubled as the band's own monitor. It projected startlingly clear sound across great distances, coherent even a quarter-mile away.</p></div>
+        <div class="tl fade"><div class="yr">1974 →</div><h3>Beautiful and impossible</h3><p>It demanded enormous power, two full crews, and days to assemble. Retired within a year, it remains a landmark in the history of live audio — proof that a crowd could share not just a concert but a single, unified field of sound.</p></div>
+      </div>
+    </div>
+  </section>
+
+  <hr class="rule"/>
+
+  <!-- EQUIPMENT -->
+  <section id="equipment">
+    <div class="wrap">
+      <span class="kicker fade">Annotated Equipment</span>
+      <h2 class="sec fade">What the room is made of</h2>
+      <p class="lead fade">The installation abstracts real components into sculptural forms. Each card annotates the engineering intent behind a part of the array.</p>
+
+      <div class="grid">
+        <article class="card fade">
+          <svg class="card-icon" viewBox="0 0 44 44" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="6" y="4" width="32" height="36" rx="3"/><circle cx="22" cy="16" r="7"/><circle cx="22" cy="31" r="4"/></svg>
+          <div class="spec">Component 01</div>
+          <h3>Driver Column</h3>
+          <p>A vertical stack of matched loudspeakers, each cell reproducing only a narrow band of frequencies so it never has to compromise. Stacked, they form the “wall.”</p>
+          <div class="stats"><span class="chip">Full-range</span><span class="chip">Line-source</span><span class="chip">Modular</span></div>
+        </article>
+
+        <article class="card fade">
+          <svg class="card-icon" viewBox="0 0 44 44" fill="none" stroke="currentColor" stroke-width="1.4"><path d="M4 22h8l4-12 6 24 5-16 3 8h10"/></svg>
+          <div class="spec">Component 02</div>
+          <h3>Active Crossover</h3>
+          <p>Divides the incoming signal into frequency bands before amplification, sending highs, mids and lows to their own dedicated banks. The key to a clean, un-muddied field.</p>
+          <div class="stats"><span class="chip">4-way split</span><span class="chip">Pre-amp</span><span class="chip">Phase-aligned</span></div>
+        </article>
+
+        <article class="card fade">
+          <svg class="card-icon" viewBox="0 0 44 44" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="5" y="10" width="34" height="24" rx="3"/><circle cx="14" cy="22" r="4"/><circle cx="26" cy="22" r="4"/><path d="M33 17v10"/></svg>
+          <div class="spec">Component 03</div>
+          <h3>Power Amplifiers</h3>
+          <p>Rows of amplifiers deliver the immense, clean headroom the array requires. Generous power reserves keep transients sharp instead of clipping into distortion.</p>
+          <div class="stats"><span class="chip">High headroom</span><span class="chip">Low noise</span><span class="chip">Redundant</span></div>
+        </article>
+
+        <article class="card fade">
+          <svg class="card-icon" viewBox="0 0 44 44" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="6" y="6" width="32" height="32" rx="3"/><path d="M12 14h20M12 22h20M12 30h20"/><circle cx="16" cy="14" r="1.6" fill="currentColor"/><circle cx="24" cy="22" r="1.6" fill="currentColor"/><circle cx="20" cy="30" r="1.6" fill="currentColor"/></svg>
+          <div class="spec">Component 04</div>
+          <h3>Mixing Console</h3>
+          <p>The soundboard, positioned out in the audience, where the engineer balances each instrument's channel. Every source keeps its own path to its own speakers.</p>
+          <div class="stats"><span class="chip">Multi-channel</span><span class="chip">Front-of-house</span><span class="chip">Per-source</span></div>
+        </article>
+
+        <article class="card fade">
+          <svg class="card-icon" viewBox="0 0 44 44" fill="none" stroke="currentColor" stroke-width="1.4"><path d="M22 6v32M10 14v16M34 14v16M4 20v8M40 20v8"/></svg>
+          <div class="spec">Component 05</div>
+          <h3>Differential Mic Rig</h3>
+          <p>Paired microphones cancel a vocalist's own monitor bleed, so the singer can stand directly in front of the wall without a feedback howl — letting the array serve as its own monitor.</p>
+          <div class="stats"><span class="chip">Noise-cancelling</span><span class="chip">On-axis</span><span class="chip">Feedback-safe</span></div>
+        </article>
+
+        <article class="card fade">
+          <svg class="card-icon" viewBox="0 0 44 44" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="8" y="8" width="28" height="28"/><path d="M8 8l28 28M36 8L8 36"/></svg>
+          <div class="spec">Component 06</div>
+          <h3>Scaffold & Rigging</h3>
+          <p>The structural steel that holds tons of cabinetry safely upright. Assembling and striking the tower took a dedicated crew and the better part of a day at each stop.</p>
+          <div class="stats"><span class="chip">Load-rated</span><span class="chip">Tour-portable</span><span class="chip">Two crews</span></div>
+        </article>
+      </div>
+    </div>
+  </section>
+
+  <hr class="rule"/>
+
+  <!-- GLOSSARY -->
+  <section id="glossary">
+    <div class="wrap">
+      <span class="kicker fade">Acoustics Glossary</span>
+      <h2 class="sec fade">A short lexicon of the field</h2>
+      <p class="lead fade">The vocabulary a listener needs to read this room. Open a term to expand its definition.</p>
+
+      <div class="fade" style="margin-top:30px">
+        <details class="gl"><summary><span><span class="term">Line Source</span> <span class="pron"> · ˈlīn sȯrs</span></span><span class="plus">+</span></summary><div class="def">A tall vertical column of drivers that behaves as a single continuous emitter. Its sound spreads cylindrically rather than spherically, so level falls off far more slowly with distance — the physical reason a wall of sound stays coherent across a large field.</div></details>
+        <details class="gl"><summary><span><span class="term">Crossover</span> <span class="pron"> · ˈkrȯs-ˌō-vər</span></span><span class="plus">+</span></summary><div class="def">The circuit that splits a full-range signal into frequency bands and routes each to speakers built for it. An <em>active</em> crossover does this before amplification, giving each band its own clean amplifier and eliminating the interference that muddies a shared driver.</div></details>
+        <details class="gl"><summary><span><span class="term">Headroom</span> <span class="pron"> · ˈhed-ˌrüm</span></span><span class="plus">+</span></summary><div class="def">The margin between the average signal level and the point of distortion. Abundant headroom lets sudden transients — a drum hit, a plucked string — reproduce cleanly instead of clipping, which is why the system carried far more power than its average output required.</div></details>
+        <details class="gl"><summary><span><span class="term">Phase Coherence</span> <span class="pron"> · ˈfāz kō-ˈhir-əns</span></span><span class="plus">+</span></summary><div class="def">When multiple drivers radiate the same waveform in step, their outputs reinforce rather than cancel. Aligning phase across hundreds of speakers is what turns a crowd of cabinets into one unified sound field instead of a smear of echoes.</div></details>
+        <details class="gl"><summary><span><span class="term">Standing Wave</span> <span class="pron"> · ˈstan-diŋ ˈwāv</span></span><span class="plus">+</span></summary><div class="def">A stationary pattern of loud and quiet zones formed when reflected sound overlaps the source. Large indoor systems fight standing waves with directivity and placement; outdoors, the open air lets the field radiate freely.</div></details>
+        <details class="gl"><summary><span><span class="term">Directivity</span> <span class="pron"> · də-ˌrek-ˈti-və-tē</span></span><span class="plus">+</span></summary><div class="def">How tightly a source focuses its energy in a given direction. High directivity concentrates sound toward the audience and away from reflective surfaces, improving clarity and reducing the feedback that plagues open-stage vocals.</div></details>
+        <details class="gl"><summary><span><span class="term">Transient</span> <span class="pron"> · ˈtran(t)-sh(ē-)ənt</span></span><span class="plus">+</span></summary><div class="def">The brief, sharp attack at the onset of a sound — the leading edge that carries much of its perceived realism. Preserving transients requires fast, un-clipped reproduction, one of the array's central design goals.</div></details>
+      </div>
+    </div>
+  </section>
+</main>
+
+<footer>
+  <div class="wrap">
+    <span class="kicker">Wall of Sound · An Immersive Room</span>
+    <p style="font-family:'Didot',Georgia,serif;font-size:clamp(22px,4vw,30px);margin:14px 0 0">“Not amplification — architecture.”</p>
+    <p class="disc"><strong style="color:var(--gold);font-weight:400">On honesty:</strong> This is a silent visual installation. It plays no audio and simulates none. The “Demo Signal” control animates a <em>modeled</em> signal path — an illustration of how the system distributed sound — and produces no sound whatsoever. All motion can be stopped with Quiet Mode without losing any of the design.</p>
+    <p class="disc" style="margin-top:18px">Keyboard: <span class="kbd">1</span>–<span class="kbd">4</span> switch perspectives · <span class="kbd">D</span> demo signal · <span class="kbd">Q</span> quiet mode · <span class="kbd">E</span> explode · <span class="kbd">← →</span> cycle perspective · <span class="kbd">Tab</span> to traverse controls.</p>
+  </div>
+</footer>
+
+<div class="quiet-flag" role="status" aria-live="polite">Quiet Mode · Motion Stilled</div>
+
+<script>
+(function(){
+  "use strict";
+  var body = document.body;
+
+  /* ---------- perspective data ---------- */
+  var P = {
+    stage: {
+      idx:"01", name:"On Stage", label:"On Stage",
+      body:"You stand where the musicians stood — with the wall directly behind you. Here the array is a monitor: your own voice returns to you clean and immediate, and the field radiates outward past your shoulders into the dark. The sensation is of being inside the source, not in front of it.",
+      char:"Enveloping", color:"Amber Core", dist:"0 m · at the array", image:"Behind & around",
+      amb1:"#c9a961", amb2:"#7d6a45", field:"#d8b869",
+      listenY:76, listenX:240, you:"MUSICIAN", signal:"M240,300 C240,180 240,120 240,66"
+    },
+    rail: {
+      idx:"02", name:"The Front Rail", label:"Front Rail",
+      body:"Pressed against the barrier, you are inside the near field. Individual driver columns are still resolvable — you can almost point to where each instrument lives on the wall. The image is enormous and physical, the transients arriving as pressure you feel before you name it.",
+      char:"Immediate · Physical", color:"Warm Gold", dist:"6 m · near field", image:"Wall-wide, resolvable",
+      amb1:"#c9a961", amb2:"#8a6f3a", field:"#c9a961",
+      listenY:150, listenX:240, you:"YOU", signal:"M240,320 C240,220 240,150 240,66"
+    },
+    board: {
+      idx:"03", name:"At the Soundboard", label:"Soundboard",
+      body:"Out in the crowd at front-of-house, you hear the mix as the engineer balances it. The columns have merged into a single coherent panorama; instruments occupy stable positions across a wide, even stage. This is the sweet spot the whole system was tuned to serve.",
+      char:"Balanced · Coherent", color:"Bronze Neutral", dist:"30 m · far field", image:"Unified panorama",
+      amb1:"#b79a5e", amb2:"#6f5d3c", field:"#a98f57",
+      listenY:250, listenX:240, you:"ENGINEER", signal:"M240,340 C240,240 240,150 240,66"
+    },
+    hill: {
+      idx:"04", name:"The Hillside", label:"Hillside",
+      body:"Far back on the grass, a quarter-mile from the array, the wall proves its purpose. Level has barely fallen; clarity survives the distance where an ordinary system would have collapsed into mush. The field is now a shared atmosphere binding thousands of listeners into one audience.",
+      char:"Vast · Communal", color:"Cool Dusk", dist:"400 m · distant field", image:"Atmospheric, shared",
+      amb1:"#8f9bb0", amb2:"#5d6b52", field:"#9aa6b8",
+      listenY:320, listenX:240, you:"CROWD", signal:"M240,352 C240,250 240,150 240,66"
+    }
+  };
+  var order = ["stage","rail","board","hill"];
+  var current = "rail";
+
+  var tabs = Array.prototype.slice.call(document.querySelectorAll('.tab'));
+  var el = {
+    kicker:document.getElementById('pKicker'), title:document.getElementById('pTitle'),
+    bodyt:document.getElementById('pBody'), char:document.getElementById('mChar'),
+    color:document.getElementById('mColor'), dist:document.getElementById('mDist'),
+    image:document.getElementById('mImage'), stageName:document.getElementById('stageName'),
+    listener:document.getElementById('listener'), you:document.getElementById('youTxt'),
+    dsignal:document.getElementById('dsignal')
+  };
+
+  function apply(key){
+    var d = P[key]; if(!d) return;
+    current = key;
+    document.documentElement.style.setProperty('--amb1', d.amb1);
+    document.documentElement.style.setProperty('--amb2', d.amb2);
+    document.documentElement.style.setProperty('--amb-glow', hexA(d.amb1,.18));
+    document.documentElement.style.setProperty('--field', d.field);
+
+    el.kicker.textContent = "Perspective "+d.idx;
+    el.title.textContent = d.name;
+    el.bodyt.textContent = d.body;
+    el.char.textContent = d.char;
+    el.color.textContent = d.color;
+    el.dist.textContent = d.dist;
+    el.image.textContent = d.image;
+    el.stageName.textContent = d.label;
+    el.you.textContent = d.you;
+    el.dsignal.setAttribute('d', d.signal);
+
+    // move listener marker
+    el.listener.setAttribute('transform','translate('+(d.listenX-240)+','+(d.listenY-250)+')');
+
+    tabs.forEach(function(t){
+      var on = t.getAttribute('data-p')===key;
+      t.setAttribute('aria-selected', on?'true':'false');
+    });
+  }
+
+  // convert hex + alpha -> rgba
+  function hexA(hex,a){
+    var h=hex.replace('#',''); if(h.length===3){h=h[0]+h[0]+h[1]+h[1]+h[2]+h[2];}
+    var r=parseInt(h.substr(0,2),16),g=parseInt(h.substr(2,2),16),b=parseInt(h.substr(4,2),16);
+    return 'rgba('+r+','+g+','+b+','+a+')';
+  }
+
+  tabs.forEach(function(t){
+    t.addEventListener('click', function(){ apply(t.getAttribute('data-p')); });
+    t.addEventListener('keydown', function(e){
+      if(e.key==='ArrowRight'||e.key==='ArrowLeft'){
+        e.preventDefault();
+        var i=order.indexOf(current);
+        i = e.key==='ArrowRight' ? (i+1)%order.length : (i-1+order.length)%order.length;
+        apply(order[i]); tabs[i].focus();
+      }
+    });
+  });
+
+  /* ---------- demo signal ---------- */
+  var demoBtn=document.getElementById('demoBtn');
+  var demoState=document.getElementById('demoState');
+  var noteTxt=document.getElementById('noteTxt');
+  function setDemo(on){
+    body.classList.toggle('demo-on', on);
+    demoBtn.setAttribute('aria-pressed', on?'true':'false');
+    demoState.textContent = on ? 'signal active (visual only)' : 'signal idle';
+    noteTxt.textContent = on
+      ? 'Demo Signal on — animating the MODELED signal path. No audio is produced; this is a silent illustration.'
+      : 'Visual simulation — this installation makes no sound. Toggle “Demo Signal” to animate the modeled signal path.';
+  }
+  demoBtn.addEventListener('click', function(){ setDemo(!body.classList.contains('demo-on')); });
+
+  /* ---------- quiet mode ---------- */
+  var quietBtn=document.getElementById('quietBtn');
+  function setQuiet(on){
+    body.classList.toggle('quiet', on);
+    quietBtn.setAttribute('aria-pressed', on?'true':'false');
+    quietBtn.textContent = on ? 'Quiet Mode · On' : 'Quiet Mode';
+  }
+  quietBtn.addEventListener('click', function(){ setQuiet(!body.classList.contains('quiet')); });
+
+  /* ---------- explode ---------- */
+  var explodeBtn=document.getElementById('explodeBtn');
+  var explodeStage=document.getElementById('explodeStage');
+  explodeBtn.addEventListener('click', function(){
+    var on = explodeStage.classList.toggle('exploded');
+    explodeBtn.setAttribute('aria-pressed', on?'true':'false');
+    explodeBtn.textContent = on ? '⤡ Reassemble the Column' : '⤢ Explode the Column';
+  });
+
+  /* ---------- keyboard shortcuts ---------- */
+  document.addEventListener('keydown', function(e){
+    if(/^(INPUT|TEXTAREA|SUMMARY)$/.test((e.target.tagName||''))) return;
+    var k=e.key.toLowerCase();
+    if(k==='1'){apply('stage');}
+    else if(k==='2'){apply('rail');}
+    else if(k==='3'){apply('board');}
+    else if(k==='4'){apply('hill');}
+    else if(k==='d'){setDemo(!body.classList.contains('demo-on'));}
+    else if(k==='q'){setQuiet(!body.classList.contains('quiet'));}
+    else if(k==='e'){explodeBtn.click();}
+    else if(e.key==='ArrowRight'||e.key==='ArrowLeft'){
+      if(document.activeElement && document.activeElement.classList.contains('tab')) return;
+      var i=order.indexOf(current);
+      i = e.key==='ArrowRight' ? (i+1)%order.length : (i-1+order.length)%order.length;
+      apply(order[i]);
+    }
+  });
+
+  /* ---------- build the array cells (hero) ---------- */
+  (function buildArray(){
+    var g=document.getElementById('array');
+    var cols=[[300,360],[360,460],[460,560],[560,620]];
+    var svgns="http://www.w3.org/2000/svg";
+    for(var c=0;c<4;c++){
+      var x0=cols[c][0], x1=cols[c][1], w=x1-x0-8;
+      var rows = 10;
+      for(var r=0;r<rows;r++){
+        var y=78 + r*(384/rows);
+        var cell=document.createElementNS(svgns,'rect');
+        cell.setAttribute('class','speaker-cell');
+        cell.setAttribute('x', x0+4); cell.setAttribute('y', y);
+        cell.setAttribute('width', w); cell.setAttribute('height', (384/rows)-6);
+        cell.setAttribute('rx','2');
+        var op = 0.10 + Math.random()*0.28;
+        cell.setAttribute('fill','var(--field)');
+        cell.setAttribute('fill-opacity', op.toFixed(2));
+        cell.setAttribute('stroke','var(--bronze)');
+        cell.setAttribute('stroke-opacity','.35');
+        // speaker cone hint
+        g.appendChild(cell);
+        var cone=document.createElementNS(svgns,'circle');
+        cone.setAttribute('cx', x0+4+w/2); cone.setAttribute('cy', y+(384/rows-6)/2);
+        cone.setAttribute('r', Math.min(w,384/rows-6)/2 - 3);
+        cone.setAttribute('fill','none'); cone.setAttribute('stroke','var(--field)');
+        cone.setAttribute('stroke-opacity','.25');
+        g.appendChild(cone);
+      }
+    }
+  })();
+
+  /* ---------- scroll entrances ---------- */
+  var io = ('IntersectionObserver' in window) ? new IntersectionObserver(function(entries){
+    entries.forEach(function(en){ if(en.isIntersecting){ en.target.classList.add('in'); io.unobserve(en.target); } });
+  }, {threshold:0.12, rootMargin:'0px 0px -8% 0px'}) : null;
+  var fades=document.querySelectorAll('.fade');
+  if(io){ fades.forEach(function(f){io.observe(f);}); }
+  else { fades.forEach(function(f){f.classList.add('in');}); }
+
+  /* stagger siblings a touch */
+  document.querySelectorAll('.grid, .timeline, .persp-tabs').forEach(function(parent){
+    Array.prototype.slice.call(parent.children).forEach(function(ch,i){
+      ch.style.animationDelay = (i*0.09)+'s';
+    });
+  });
+
+  /* init */
+  apply('rail');
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/verification/grateful-dead-gallery.e2e.js b/verification/grateful-dead-gallery.e2e.js
new file mode 100644
index 0000000..272c548
--- /dev/null
+++ b/verification/grateful-dead-gallery.e2e.js
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+const { chromium } = require('/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright');
+
+const ids = [
+  'long-strange-archive',
+  'deadhead-print-lab',
+  'wall-of-sound',
+  'routes-residencies-rituals',
+  'objects-golden-road',
+];
+const titles = [
+  'The Long Strange Archive',
+  'Deadhead Print Lab',
+  'Wall of Sound: An Immersive Room',
+  'Routes, Residencies & Rituals',
+  'Objects in the Golden Road',
+];
+
+(async () => {
+  const base = process.env.GALLERY_URL || 'http://127.0.0.1:8792/';
+  const browser = await chromium.launch({ executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: true });
+  const page = await browser.newPage({ viewport: { width: 1440, height: 1100 } });
+  const errors = [];
+  page.on('pageerror', error => errors.push(String(error)));
+  page.on('console', message => {
+    if (message.type() === 'error' && !/^Failed to load resource:/.test(message.text())) errors.push(message.text());
+  });
+  await page.goto(base, { waitUntil: 'networkidle' });
+  await page.waitForSelector('.card');
+  if (await page.locator('.card').count() !== 6) throw new Error('expected six gallery cards');
+  const text = await page.locator('#grid').innerText();
+  for (const title of titles) if (!text.includes(title)) throw new Error(`missing gallery title: ${title}`);
+
+  for (const id of ids) {
+    const card = page.locator(`.card[data-id="${id}"]`);
+    const image = card.locator('img');
+    if (!await image.evaluate(node => node.complete && node.naturalWidth > 0)) throw new Error(`${id} cover failed`);
+    await card.locator('.preview').click();
+    await page.waitForSelector('#overlay.open');
+    const frame = page.frameLocator('#ovFrame');
+    await frame.locator('body').waitFor();
+    const bodyText = await frame.locator('body').innerText();
+    if (bodyText.trim().length < 100) throw new Error(`${id} preview is empty`);
+    await page.locator('#ovClose').click();
+  }
+
+  await page.selectOption('#sort', 'newest');
+  await page.locator('#density').fill('2');
+  const persisted = await page.evaluate(() => ({ sort: localStorage.getItem('gallery:sort'), density: localStorage.getItem('gallery:density') }));
+  if (persisted.sort !== 'newest' || persisted.density !== '2') throw new Error(`controls did not persist: ${JSON.stringify(persisted)}`);
+  await page.screenshot({ path: 'verification/grateful-dead-gallery-live.png', fullPage: true });
+  if (errors.length) throw new Error(`browser errors: ${errors.join(' | ')}`);
+  console.log(JSON.stringify({ pass: true, cards: 6, deadBuilds: ids.length, previews: ids.length, persisted, browserErrors: 0 }));
+  await browser.close();
+})().catch(error => { console.error(error.stack || error); process.exit(1); });

← 49e3ef9 deploy: make deploy.sh idempotent + SSL-safe (installs HTTPS  ·  back to Gallery Agentabrams  ·  verify Grateful Dead gallery production release 1ae237c →