* { margin:0; padding:0; box-sizing:border-box; }
/* position:fixed + inset:0 (not just overflow:hidden) — mobile Safari/Chrome
   can still rubber-band/overscroll-bounce a document that's merely
   overflow:hidden, and that bounce can visually displace position:fixed
   children (like #colorBarWidget, #mpWidget, #miniPlayer) out from under
   the visible viewport for a moment. Pinning html/body outright removes
   any scrollable surface for the bounce to act on in the first place. */
html, body { position:fixed; inset:0; overflow:hidden; background:#000; width:100%; height:100%; height:100dvh; }

canvas { position:fixed; inset:0; width:100vw; height:100vh; height:100dvh; display:block; touch-action:none; }
#flowCanvas { pointer-events:none; display:none; z-index:1; }

/* #programFrame — the shell's (index.php) persistent iframe. Holds whichever
   of the three standalone WebGL programs (interference/eq/grid) is active;
   swapped by js/program_nav.js. Sits at the same z-index tier as a bare
   canvas would, well below all the shared chrome (music widget, site menu,
   program nav, gear panel) which the shell renders outside of it.
   height:100dvh (100vh kept first as an older-browser fallback, overridden
   by the dvh line below wherever it's supported) — 100vh on mobile is
   calculated against the LARGER viewport as if the browser's address bar
   were permanently hidden, which can render this iframe taller than what's
   actually visible; content pinned to ITS bottom edge (like the 38px-tall
   gear icon) then sits below the real fold. dvh tracks the true visible
   viewport as the address bar shows/hides instead. */
#programFrame { position:fixed; inset:0; width:100vw; height:100vh; height:100dvh; display:block; border:0; background:#000; }

/* ── bottom control bar ── */
#controls {
  position:fixed; bottom:0; left:0; right:0;
  z-index:20;
  display:flex; flex-direction:column; align-items:stretch;
}

/* collapse arrow — sits above the button row */
#btnCollapseArrow {
  height:24px; background:rgba(0,0,0,0.6);
  border-top:1px solid #2a2a2a;
  color:#666; font-size:10px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  touch-action:manipulation; user-select:none;
  letter-spacing:0.05em;
}

/* scrollable button row */
#btnStack {
  display:flex; flex-direction:row; gap:10px;
  align-items:center; padding:8px 14px;
  overflow-x:auto; overflow-y:hidden;
  background:rgba(0,0,0,0.82);
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  transition:max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease;
  max-height:70px; opacity:1;
  touch-action:pan-x;
}
#btnStack::-webkit-scrollbar { display:none; }
#btnStack.collapsed { max-height:0; opacity:0; pointer-events:none; padding:0 14px; }

.cb {
  width:46px; height:46px; border-radius:50%; flex-shrink:0;
  background:rgba(20,20,20,0.95); border:1px solid #333;
  color:#999; font-size:13px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  touch-action:manipulation; user-select:none; font-family:monospace;
}
.cb.active { background:rgba(50,50,50,0.95); border-color:#777; color:#fff; }

/* ── settings panel — above button bar ── */
#panel {
  position:fixed; top:58px; bottom:190px; right:10px; left:10px;
  background:rgba(6,6,6,0.85);
  border:1px solid #252525; border-radius:10px;
  font-family:monospace; font-size:12px; color:#888;
  display:flex; flex-direction:column;
  z-index:19;
  transition:opacity 0.2s, transform 0.15s;
  overflow:hidden;
}
#panel.hidden { opacity:0; pointer-events:none; transform:translateY(10px); }

/* tab bar — sticky at top of panel */
#tabBar {
  display:flex; gap:2px; padding:6px 8px 0;
  flex-shrink:0; border-bottom:1px solid #1e1e1e;
  background:rgba(6,6,6,0.95);
  position:sticky; top:0; z-index:5;
}
.tab {
  flex:1; text-align:center; padding:5px 2px; font-size:10px;
  font-family:monospace; color:#444; cursor:pointer;
  border-radius:4px 4px 0 0; border:1px solid transparent;
  touch-action:manipulation; user-select:none;
}
.tab.on { color:#ccc; border-color:#333; background:#111; }

/* tab content areas */
.tabContent {
  overflow-y:auto; overflow-x:hidden;
  padding:10px 12px 12px;
  display:flex; flex-direction:column; gap:11px;
  flex:1 1 auto; min-height:0;
  touch-action:pan-y;
}
.tabContent.hidden { display:none; }

.sec {
  color:#3a3a3a; font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  border-bottom:1px solid #1a1a1a; padding-bottom:3px; margin-bottom:6px;
}

/* presets */
.presets { display:grid; grid-template-columns:repeat(4,1fr); gap:5px; }
.presets button {
  background:#0e0e0e; border:1px solid #252525; color:#666;
  font-family:monospace; font-size:11px; padding:7px 2px;
  border-radius:4px; cursor:pointer; touch-action:manipulation;
}
.presets button.active { border-color:#666; color:#ddd; background:#1a1a1a; }

/* sliders */
.sl { display:flex; align-items:center; gap:7px; }
.sl label { color:#555; width:62px; flex-shrink:0; font-size:11px; }
.sl input[type=range] {
  flex:1; -webkit-appearance:none; height:3px;
  background:#1e1e1e; outline:none; border-radius:2px; touch-action:pan-y;
}
.sl input[type=range]::-webkit-slider-thumb {
  -webkit-appearance:none; width:22px; height:22px; border-radius:50%; background:#bbb;
}
.sl span { color:#555; width:36px; text-align:right; flex-shrink:0; font-size:11px; }
.slgroup { display:flex; flex-direction:column; gap:8px; }

/* custom slider overlay */
.cslider-wrap { flex:1; position:relative; height:28px; display:flex; align-items:center; }
.cslider-track { position:absolute; left:0; right:0; height:3px; background:#1e1e1e; border-radius:2px; }
.cslider-fill  { position:absolute; left:0; height:3px; background:#444; border-radius:2px; }
.cslider-thumb {
  position:absolute; width:22px; height:22px; border-radius:50%; background:#bbb;
  top:50%; transform:translate(-50%,-50%); cursor:pointer;
  touch-action:none;
}
.cslider-thumb.active { background:#fff; }

/* toggles */
.toggles { display:grid; grid-template-columns:1fr 1fr 1fr; gap:5px; }
.tog {
  background:#0e0e0e; border:1px solid #1e1e1e; color:#555;
  font-family:monospace; font-size:10px; padding:6px 4px;
  border-radius:4px; cursor:pointer; text-align:center;
  touch-action:manipulation; user-select:none;
  position:relative; z-index:2;
}
.tog.on { border-color:#555; color:#ccc; background:#181818; }

/* lfo blocks */
.lfo-block {
  border:1px solid #1a1a1a; border-radius:4px; padding:6px;
  display:flex; flex-direction:column; gap:5px;
}
.lfo-block .lfo-header { display:flex; gap:5px; align-items:center; }
.lfo-block select {
  flex:1; background:#0e0e0e; border:1px solid #222; color:#666;
  font-family:monospace; font-size:10px; padding:3px 4px;
  border-radius:3px; -webkit-appearance:none;
}
.lfo-block .lfo-shape {
  background:#0e0e0e; border:1px solid #222; color:#666;
  font-family:monospace; font-size:10px; padding:3px 8px;
  border-radius:3px; cursor:pointer; touch-action:manipulation; flex-shrink:0;
}
.lfo-block .lfo-rate { display:flex; align-items:center; gap:5px; }
.lfo-block .lfo-rate label { color:#555; font-size:10px; width:36px; flex-shrink:0; }
.lfo-block .lfo-rate input[type=range] {
  flex:1; -webkit-appearance:none; height:3px; background:#1e1e1e;
  outline:none; border-radius:2px; touch-action:pan-y;
}
.lfo-block .lfo-rate input[type=range]::-webkit-slider-thumb {
  -webkit-appearance:none; width:18px; height:18px; border-radius:50%; background:#bbb;
}
.lfo-block .lfo-rate span { color:#555; font-size:10px; width:30px; text-align:right; flex-shrink:0; }

/* palette swatches */
.psw {
  width:32px; height:32px; border:1px solid #333; background:none;
  padding:0; border-radius:3px; cursor:pointer;
}

/* misc */
#recDot {
  position:fixed; top:12px; left:12px; width:10px; height:10px;
  border-radius:50%; background:#f00; z-index:30;
  display:none; animation:blink 1s infinite;
}
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.2} }
#savedMsg {
  position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
  background:rgba(0,0,0,0.85); border:1px solid #444; border-radius:6px;
  color:#ccc; font-family:monospace; font-size:13px;
  padding:12px 20px; z-index:30; pointer-events:none;
  opacity:0; transition:opacity 0.3s;
}
#savedMsg.show { opacity:1; }
.rec-select {
  background:#0e0e0e; border:1px solid #222; color:#666;
  font-family:monospace; font-size:10px; padding:4px; border-radius:3px; flex:1;
}
.name-input {
  flex:1; background:#0e0e0e; border:1px solid #222; color:#888;
  font-family:monospace; font-size:11px; padding:5px 7px; border-radius:3px; outline:none;
}
.snap-slot { display:flex; align-items:center; gap:5px; }
.snap-slot button {
  background:#0e0e0e; border:1px solid #222; color:#666;
  font-family:monospace; font-size:10px; padding:4px 6px;
  border-radius:3px; cursor:pointer; touch-action:manipulation; flex-shrink:0;
}
.snap-slot button.saved { border-color:#555; color:#ccc; }
.snap-slot .snap-label {
  color:#444; font-size:10px; flex:1; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap;
}

/* Public front page lockdown — no controls, no panel, pinch/zoom only.
   Applied via body.il-public, set server-side in inc/app_shell.php. */
body.il-public #controls,
body.il-public #panel,
body.il-public #recDot,
body.il-public #savedMsg,
body.il-public #pnToast {
  display: none !important;
  pointer-events: none !important;
}


/* ===================== Music widget (transparent overlay) v2 =====================
   Namespaced with mp- / mp* to avoid colliding with the settings-panel classes
   (.tab, .sec, etc). Sits above the WebGL canvas, independent of il-public/admin
   mode — visible on both the front page and the editor. Styled to echo the
   original standalone music player (dark title bar, tab strip, playlist rows)
   but translucent so the interference pattern shows through. */
/* z-index:70 — deliberately above #colorBarWidget (64, the gear panel's
   preset-arrow/kaleidoscope tab, interference-only) so the music player is
   always the topmost overlay ("transcendent") no matter which program is
   active, instead of only on eq/grid (which have no gear panel to compete
   with). See also #miniPlayer and #aiConsent below, kept above it too. */
#mpWidget { position:fixed; top:12px; right:12px; z-index:70; font-family:'Maven Pro', monospace, sans-serif; }

#mpTab {
  width:38px; height:38px; border-radius:2px;
  background:rgba(20,20,20,0.75);
  color:#eee; display:flex; align-items:center; justify-content:center;
  font-size:19px; cursor:pointer; border:1px solid rgba(255,255,255,0.14);
  user-select:none; touch-action:manipulation;
}
#mpTab:active { transform:scale(0.94); }

/* Panel is positioned independently of the icon so it can span the screen
   width with even margins, rather than hugging the icon's corner. */
#mpPanel {
  position:fixed; top:58px; left:16px; right:16px; bottom:150px; margin:0 auto;
  max-width:640px;
  background:rgba(10,10,10,0.55); backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,0.14); border-radius:8px;
  overflow:hidden; display:flex; flex-direction:column;
  transition:opacity .18s, transform .18s;
  box-shadow:0 8px 30px rgba(0,0,0,0.35);
}
#mpPanel.mp-hidden { opacity:0; pointer-events:none; transform:translateY(-8px); }

/* title bar — echoes the original #row-title */
#mpHeader {
  background:rgba(20,20,20,0.7); color:#eee;
  display:flex; align-items:center; justify-content:space-between;
  padding:9px 12px; letter-spacing:0.1em; text-transform:uppercase;
  font-size:11px; font-weight:500; border-bottom:1px solid rgba(255,255,255,0.08);
  flex-shrink:0;
}
#mpClose { cursor:pointer; font-size:16px; color:#999; line-height:1; padding:0 2px; }
#mpClose:hover { color:#fff; }

/* tab strip — echoes original #row-tabs, with underline accent */
#mpTabsRow {
  display:flex; overflow-x:auto; scrollbar-width:none; flex-shrink:0;
  background:rgba(20,20,20,0.4); border-bottom:1px solid rgba(255,255,255,0.08);
}
#mpTabsRow::-webkit-scrollbar { display:none; }
.mp-yr {
  flex-shrink:0; padding:8px 12px; font-size:12px; font-weight:500;
  letter-spacing:0.04em; color:#999; cursor:pointer; user-select:none;
  border-bottom:2px solid transparent; transition:color .15s, border-color .15s;
}
.mp-yr:hover { color:#ccc; }
.mp-yr.active { color:#fff; font-weight:700; border-bottom-color:#4af; }

/* info row — echoes original #row-info */
#mpInfo {
  display:flex; justify-content:space-between; align-items:center;
  padding:7px 12px; font-size:11px; color:#aaa; flex-shrink:0;
  background:rgba(30,30,30,0.35); border-bottom:1px solid rgba(255,255,255,0.06);
}
#mpMode {
  font-size:11px; font-weight:700; color:#aaa;
  border:1px solid rgba(255,255,255,0.22); border-radius:3px;
  padding:3px 9px; cursor:pointer; letter-spacing:0.04em; user-select:none;
  transition:color .15s, border-color .15s, background .15s;
}
#mpMode.on { color:#4af; border-color:#4af; background:rgba(74,170,255,0.1); }

/* playlist — echoes original .song-item rows. flex:1 fills whatever space
   is left in the panel (which itself now sizes to fit between the top icon
   row and the bottom cluster) instead of clipping at a fixed pixel height. */
#mpList { flex:1 1 auto; min-height:0; overflow-y:auto; }
.mp-song {
  padding:8px 12px; font-size:12.5px; color:#ddd; cursor:pointer;
  border-bottom:1px solid rgba(255,255,255,0.06);
  display:flex; align-items:center; gap:9px; transition:background .1s;
}
.mp-song:hover { background:rgba(255,255,255,0.07); }
.mp-song.playing { background:rgba(255,255,255,0.07); color:#4af; font-weight:700; }
.mp-num { color:#777; font-size:10.5px; min-width:18px; text-align:right; flex-shrink:0; }
.mp-song.playing .mp-num { color:#4af; }
.mp-empty { padding:14px 12px; color:#666; font-size:11px; }

/* player row — custom controls (native <audio controls> can't be restyled on
   Android Chrome, it just shows the OS media widget, so we build our own) */
#mpPlayerRow { display:flex; align-items:center; gap:10px; padding:10px 12px; background:rgba(20,20,20,0.55); flex-shrink:0; }
#mpPlayBtn {
  width:30px; height:30px; border-radius:50%; flex-shrink:0;
  background:rgba(255,255,255,0.12); color:#eee;
  display:flex; align-items:center; justify-content:center;
  font-size:12px; cursor:pointer; user-select:none; touch-action:manipulation;
}
#mpPlayBtn:active { transform:scale(0.92); }
#mpProgWrap {
  flex:1; height:4px; background:rgba(255,255,255,0.15); border-radius:2px;
  position:relative; cursor:pointer; touch-action:manipulation;
}
#mpProgFill { position:absolute; left:0; top:0; height:100%; width:0%; background:#4af; border-radius:2px; }
#mpTime { font-size:10px; color:#999; flex-shrink:0; min-width:74px; text-align:right; }

@media (max-width:480px) {
  #mpPanel { left:10px; right:10px; }
}

/* ===================== Preset arrows / kaleid row (inside #colorBarPanel) =====================
   spacer + centered arrow pair + kaleid pinned right: the spacer's width
   matches the kaleid button so the arrows land visually centered in the
   row, not just centered in the leftover space next to kaleid. */
#bcTopRow { display:flex; align-items:center; gap:8px; }
#bcTopRow:not(:last-child) { margin-bottom:10px; }
.bc-spacer { width:38px; flex-shrink:0; }
#bcArrows { flex:1; display:flex; justify-content:center; gap:8px; }

.pn-btn {
  width:38px; height:38px; border-radius:2px; flex-shrink:0;
  background:#0b0c0d;
  color:#dfe6e8; display:flex; align-items:center; justify-content:center;
  font-size:14px; cursor:pointer; border:1px solid rgba(255,255,255,0.2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,0.05);
  user-select:none; touch-action:manipulation;
}
.pn-btn:active { transform:scale(0.92); }
.pn-btn.active {
  color:#bdf1ff; border-color:rgba(140,230,255,0.65);
  box-shadow:0 0 9px rgba(120,220,255,0.45), inset 0 1px 0 rgba(255,255,255,0.08);
}

/* toast anchors above the card rather than the page, so it moves with it */
#pnToast {
  position:absolute; left:12px; bottom:100%; margin-bottom:8px; max-width:220px;
  background:rgba(6,7,8,0.9);
  border:1px solid rgba(255,255,255,0.18); border-radius:2px;
  color:#ccc; font-size:11px;
  padding:6px 12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  opacity:0; pointer-events:none; transition:opacity .25s;
}
#pnToast.show { opacity:1; }

/* geometry color blocks — evenly split across the full width (100%/8), in
   spectral/prismatic order. "off" isn't a separate block: conf-amt at 0
   already reads as off regardless of which mode is selected. */
/* mini player — now a standalone bar, always at the very bottom of the
   screen once a track has been picked. mini-hidden = display:none (not the
   usual opacity-based .mp-hidden) so it collapses to nothing and other
   elements can measure around it correctly when it's not showing.
   z-index:62 — deliberately LOW relative to #colorBarWidget/eq's/grid's own
   "options" buttons (64+). Those all live inside the shell's <iframe> and
   position themselves ABOVE this bar using a height read cross-frame from
   the parent (js/layout_coord.js) — if that measurement is ever a few
   pixels off, a high z-index here would let this bar visually cover them
   completely instead of just overlapping by a sliver. Keeping this modest
   means a small positioning miss degrades gracefully instead of hiding
   controls. #mpWidget/#mpPanel (the full expandable player) stay well
   above everything at 70 — that's the "transcendent" piece this is not. */
#miniPlayer {
  position:fixed; left:16px; right:16px; bottom:14px; margin:0 auto; max-width:640px;
  display:flex; align-items:center; gap:8px;
  background:rgba(8,9,10,0.72); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,0.16); border-radius:2px;
  padding:10px 12px; z-index:62; box-shadow:0 8px 30px rgba(0,0,0,0.45);
  font-family:'Maven Pro', monospace, sans-serif;
}
#miniPlayer.mini-hidden { display:none; }
#miniPlayer .pn-btn { width:30px; height:30px; font-size:11px; flex-shrink:0; }
#miniProgWrap {
  flex:1; height:2px; background:rgba(255,255,255,0.2); border-radius:0;
  position:relative; cursor:pointer; touch-action:manipulation;
}
#miniProgFill { position:absolute; left:0; top:0; height:100%; width:0%; background:#bdf1ff; box-shadow:0 0 4px rgba(120,220,255,0.6); }
#miniTime { font-size:10px; color:#888; flex-shrink:0; min-width:70px; text-align:right; }

#sqGeoRow { display:flex; gap:3px; margin-bottom:18px; }
.sq-dot {
  flex:1 1 0; height:24px; border-radius:2px; cursor:pointer;
  border:1px solid rgba(0,0,0,0.35); touch-action:manipulation;
  transition:transform .12s, border-color .12s, box-shadow .12s;
}
.sq-dot.active {
  border-color:#bdf1ff; transform:scale(1.04);
  box-shadow:0 0 8px rgba(120,220,255,0.6);
}

#sqConfAmt {
  width:100%; -webkit-appearance:none; appearance:none; height:2px; border-radius:0;
  background:rgba(255,255,255,0.2); outline:none; display:block; margin:0 0 8px;
}
#sqConfAmt::-webkit-slider-thumb {
  -webkit-appearance:none; appearance:none; width:14px; height:14px; border-radius:2px;
  background:#0b0c0d; border:1px solid rgba(140,230,255,0.65); cursor:pointer;
  box-shadow:0 0 6px rgba(120,220,255,0.45);
}
#sqConfAmt::-moz-range-thumb {
  width:14px; height:14px; border-radius:2px; background:#0b0c0d;
  border:1px solid rgba(140,230,255,0.65); cursor:pointer;
}

/* ===================== Color bar popup (gear icon, above the mini player) =====================
   Same open/close pattern as the music widget: tap the icon to toggle, tap
   outside the panel to close (both wired in color_bar.js). The tab now
   sits just above the mini player, right-aligned, reusing the exact same
   --gear-panel-bottom offset the panel itself rests at (measured live in
   layout_coord.js) so it clears the mini player by the same gap. The tab
   hides itself while the panel is open (.cb-hidden, toggled in
   color_bar.js) so the two never overlap in that shared spot.
   z-index:64 — above #miniPlayer (62) on purpose: this whole widget only
   works correctly when --gear-panel-bottom (above) has already pushed it
   clear of the mini player, but that height is measured cross-frame (this
   lives inside the shell's <iframe>, the mini player lives in the parent
   shell) via layout_coord.js — if that measurement is ever slightly off,
   being above the mini player in z-index means a small miss just means a
   sliver of visual overlap, not the gear icon disappearing under it
   entirely. Still safely below #mpWidget/#mpPanel (70), which stays the
   one truly "transcendent" overlay above everything, per design. */
#colorBarWidget { position:fixed; right:16px; bottom:var(--gear-panel-bottom, 76px); top:auto; z-index:64; font-family:'Maven Pro', monospace, sans-serif; transition:bottom .18s; }
#colorBarTab {
  width:38px; height:38px; border-radius:2px;
  background:rgba(20,20,20,0.75);
  color:#eee; display:flex; align-items:center; justify-content:center;
  cursor:pointer; border:1px solid rgba(255,255,255,0.14);
  user-select:none; touch-action:manipulation;
  transition:opacity .15s, transform .15s;
}
#colorBarTab:active { transform:scale(0.94); }
#colorBarTab.cb-hidden { opacity:0; pointer-events:none; transform:scale(0.85); }
#colorBarPanel {
  position:fixed; left:16px; right:16px; bottom:var(--gear-panel-bottom, 76px); margin:0 auto; max-width:640px;
  background:rgba(8,9,10,0.72); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,0.16); border-radius:2px;
  padding:10px 12px; z-index:64; box-shadow:0 8px 30px rgba(0,0,0,0.45);
  transition:opacity .18s, transform .18s, bottom .18s;
}
#colorBarPanel.mp-hidden { opacity:0; pointer-events:none; transform:translateY(8px); }
#colorBarPanel #sqGeoRow { margin-bottom:18px; }

/* mpPanel / siteMenuPanel height coordination: --stack-bottom is measured
   live in layout_coord.js from whatever's actually pinned at the bottom
   right now (mini player, gear panel, #controls on the editor, any
   combination, or none) — replaces a set of hand-guessed pixel offsets
   that drifted out of sync with real content, and now correctly covers
   both pages with one rule instead of separate static/dynamic variants. */
#mpPanel { bottom:var(--stack-bottom, 90px); transition:opacity .18s, transform .18s, bottom .18s; }

@media (max-width:480px) {
  #colorBarPanel, #miniPlayer { left:10px; right:10px; }
  #colorBarWidget { right:10px; }
}

/* ===================== Site menu (top-left popup) =====================
   Styled to match the music widget: solid rounded-rect icon, translucent
   panel with a dark header bar. Reuses .mp-hidden for open/close state. */
#siteMenuWidget { position:fixed; top:12px; left:12px; z-index:60; font-family:'Maven Pro', monospace, sans-serif; }
#siteMenuTab {
  width:38px; height:38px; border-radius:2px;
  background:rgba(20,20,20,0.75); color:#eee;
  display:flex; align-items:center; justify-content:center;
  cursor:pointer; border:1px solid rgba(255,255,255,0.14);
  user-select:none; touch-action:manipulation;
}
#siteMenuTab:active { transform:scale(0.94); }
#siteMenuPanel {
  position:fixed; top:12px; left:58px; width:200px; max-width:60vw;
  background:rgba(10,10,10,0.55); backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,0.14); border-radius:8px;
  overflow:hidden; display:flex; flex-direction:column;
  transition:opacity .18s, transform .18s;
  box-shadow:0 8px 30px rgba(0,0,0,0.35);
}
#siteMenuPanel.mp-hidden { opacity:0; pointer-events:none; transform:translateY(-8px); }
#siteMenuHeader {
  background:rgba(20,20,20,0.7); color:#eee;
  display:flex; align-items:center; justify-content:space-between;
  padding:9px 12px; letter-spacing:0.1em; text-transform:uppercase;
  font-size:11px; font-weight:500; border-bottom:1px solid rgba(255,255,255,0.08);
}
#siteMenuClose { cursor:pointer; font-size:16px; color:#999; line-height:1; padding:0 2px; }
#siteMenuClose:hover { color:#fff; }
#siteMenuList { display:flex; flex-direction:column; }
.sm-link {
  padding:10px 12px; font-size:12.5px; color:#ddd; text-decoration:none;
  border-bottom:1px solid rgba(255,255,255,0.06);
}
.sm-link:last-child { border-bottom:none; }
.sm-link:hover, .sm-link:active { background:rgba(255,255,255,0.08); color:#4af; }

/* ===================== Program switcher (top-left, below the hamburger) =====================
   Links between the three standalone WebGL programs (this page / eq / grid).
   Same icon-button look as every other overlay control, same blue "active"
   highlight convention as .pn-btn.active / #colorBarTab elsewhere. eq/index.php
   and grid/index.php carry their own copy of this block (kept in sync manually,
   since those two live outside the inc/ template system) so the row never
   jumps position when switching between programs. */
#programNav { position:fixed; top:58px; left:12px; z-index:60; display:flex; flex-direction:column; gap:8px; font-family:'Maven Pro', monospace, sans-serif; }
.pnav-btn {
  width:38px; height:38px; border-radius:2px;
  background:rgba(20,20,20,0.75);
  color:#eee; display:flex; align-items:center; justify-content:center;
  cursor:pointer; border:1px solid rgba(255,255,255,0.14);
  user-select:none; touch-action:manipulation; text-decoration:none;
}
.pnav-btn:active { transform:scale(0.94); }
.pnav-btn.active {
  color:#bdf1ff; border-color:rgba(140,230,255,0.65);
  box-shadow:0 0 9px rgba(120,220,255,0.45), inset 0 1px 0 rgba(255,255,255,0.08);
}

/* ===================== AI folder: tab highlight + autoplay consent =====================
   The ai tab gets a subtle accent border so it reads as "special" among the
   plain year tabs (its shuffle is locked to just this folder, and it's the
   one folder that can autoplay). */
.mp-yr-ai { color:#bdf1ff; }
.mp-yr-ai.active { border-bottom-color:#bdf1ff; }

#aiConsent {
  position:fixed; left:16px; right:16px; top:58px; margin:0 auto; max-width:400px;
  background:rgba(8,9,10,0.85); backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  border:1px solid rgba(140,230,255,0.4); border-radius:4px;
  padding:14px; z-index:75; box-shadow:0 8px 30px rgba(0,0,0,0.5);
  font-family:'Maven Pro', monospace, sans-serif;
  transition:opacity .2s, transform .2s;
}
#aiConsent.mp-hidden { opacity:0; pointer-events:none; transform:translateY(-8px); }
#aiConsentMsg { color:#dfe6e8; font-size:12px; margin-bottom:12px; line-height:1.4; }
#aiConsentRow { display:flex; gap:8px; }
.ai-btn {
  flex:1; text-align:center; padding:8px; font-size:11px; letter-spacing:0.05em;
  background:#0b0c0d; color:#aaa; border:1px solid rgba(255,255,255,0.18);
  border-radius:3px; cursor:pointer; touch-action:manipulation; user-select:none;
}
.ai-btn:active { transform:scale(0.96); }
.ai-btn.accent { color:#bdf1ff; border-color:rgba(140,230,255,0.55); }
