<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no, viewport-fit=cover">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>🎤 Building A Rockstar</title>
<!-- Open Graph + Twitter Card tags so link previews show the BAR logo
     instead of whatever image scrapers grab first off the page -->
<meta property="og:title" content="Building A Rockstar">
<meta property="og:description" content="Your pocket sherpa from seat to stage. Smart songbook, AI-powered song discovery, performance log with charts, voice technique training, and singing friends along for the journey.">
<meta property="og:image" content="https://buildingarockstar.com/images/bar-logo.png">
<meta property="og:url" content="https://buildingarockstar.com">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Building A Rockstar">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Building A Rockstar">
<meta name="twitter:description" content="For karaoke regulars and open-mic singers serious about their voice. Smart songbook, AI song picks, performance log, voice technique guides, friends along for the ride.">
<meta name="twitter:image" content="https://buildingarockstar.com/images/bar-logo.png">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#0a0a0f">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="BAR">
<link rel="apple-touch-icon" href="/images/bar-logo.png">
<link href="fonts/fonts.css" rel="stylesheet">
  <link rel="stylesheet" href="style.css">
  <!-- Third-party libs are version-pinned + Subresource-Integrity-locked: if a CDN
       is ever compromised (or serves altered bytes), the browser refuses to run the
       script instead of letting tampered code read the Supabase session from
       localStorage. crossorigin=anonymous is required for SRI on cross-origin scripts.
       Hashes verified against genuine published bytes 2026-06-05. -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js" integrity="sha512-BNaRQnYJYiPSqHHDb58B0yaPfCu+Wgds8Gp/gU33kqBtgNS4tSPHuGibyoeqMV/TJlSKda6FXzoEyYGjTe+vXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" integrity="sha512-CQBWl4fJHWbryGE+Pc7UAxWMUMNMWzWxF4SQo9CgkJIN1kx6djDQZjh3Y8SZ1d+6I+1zze6Z7kHXO7q3UyZAWw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2.107.0/dist/umd/supabase.js" integrity="sha512-RDEsmYicjuEMEkKzxQgAm4TAvnWh33+SqUadQ1VEcPcRDa7oW5faDh1nkk0kMkMh/ZmqjXa7YEyfmsZE/aXMkA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    window.VITE_SUPABASE_URL      = 'https://ktpdbyugxluqfaibfgez.supabase.co';
    window.VITE_SUPABASE_ANON_KEY = 'sb_publishable_ol6cD7H4gJ13emz-JcXSZw_JAHjfkfN';
  </script>
  <script src="supabase_client.js"></script>
<script>
window.VITE_SUPABASE_URL      = "https://ktpdbyugxluqfaibfgez.supabase.co/rest/v1/";
window.VITE_SUPABASE_ANON_KEY = "sb_publishable_ol6cD7H4gJ13emz-JcXSZw_JAHjfkfN";
</script></head>
<body style="-webkit-tap-highlight-color: transparent;">

<!-- ─── DESKTOP "STAGE" DECORATION ──────────────────────────────────────────
     Wide-screen gutters around the centered .main column. Hidden on mobile
     via CSS media query (only visible >= 900px). The center column is the
     "lit stage"; gutters are the dark venue around it. Left gutter shows
     the BAR logo as projected back-wall signage; right gutter has the
     full "BUILDING A ROCKSTAR" wordmark running vertically up the wall.
     Fixed positioning so the venue stays put while the show (the scrolling
     content) plays out. pointer-events:none so taps fall through cleanly.
     z-index:0 keeps the decoration below all sticky/positioned UI. -->
<div class="stage-gutters" aria-hidden="true">
  <div class="stage-gutter stage-gutter-left">
    <img src="/images/bar-logo.png" alt="" class="stage-gutter-logo">
  </div>
  <div class="stage-gutter stage-gutter-right">
    <span class="stage-gutter-marquee">BUILDING A ROCKSTAR</span>
  </div>
</div>

<header class="app-header">
  <!-- 2026-06-06 Apple-fy Phase 3 — header shrunk 3 rows → 2. Settings was
       dropped from here (it lives in the bottom-bar "More" hub now); the
       redundant auth-status subtitle row was removed (sign-in state shows via the
       👤 Account icon's green tint + the Account sheet). Row 1 = logo + Account +
       Find. Row 2 = Tonight's Venue (left, tap to set) + sync dot + version. -->
  <!-- Row 1: logo + account/find -->
  <div class="header-row-top">
    <div class="app-logo">
      <div class="logo-bar-row"><span>BAR</span></div>
      <div class="logo-fullname">Building A Rockstar</div>
    </div>
    <div class="header-actions">
      <button class="btn-icon" onclick="handleAuthHeaderBtn()" title="Account — sign in, profile & account settings"
          style="display:flex;flex-direction:column;align-items:center;gap:1px;font-size:16px;padding:5px 7px;">
        👤<span id="auth-acct-label" class="header-btn-label"
            style="font-size:10px;color:var(--text3);font-family:'DM Sans',sans-serif;">Account</span>
      </button>
      <button class="btn-icon" onclick="openFindModal()" title="Find anything across BAR"
          style="display:flex;flex-direction:column;align-items:center;gap:1px;font-size:16px;padding:5px 7px;">
        🔍<span class="header-btn-label"
            style="font-size:10px;color:var(--text3);font-family:'DM Sans',sans-serif;">Find</span>
      </button>
      <!-- ⚙️ Settings + ☰ More (2026-06-07 IA reorg). Settings is now its own
           top-level icon holding ONLY genuine config (Profile + Account); the
           non-settings stuff (Walkthrough, Highlight Reel, Help, Install, Your
           Data, Tags & Tools, About, Report) lives in the honest "More" hub so
           nothing is mislabeled as "Settings" anymore. -->
      <button class="btn-icon" onclick="openSettings()" title="Settings — profile & account"
          style="display:flex;flex-direction:column;align-items:center;gap:1px;font-size:16px;padding:5px 7px;">
        ⚙️<span class="header-btn-label"
            style="font-size:10px;color:var(--text3);font-family:'DM Sans',sans-serif;">Settings</span>
      </button>
      <button class="btn-icon" onclick="openMore()" title="More — walkthrough, help, data, tools & about"
          style="display:flex;flex-direction:column;align-items:center;gap:1px;font-size:16px;padding:5px 7px;">
        ☰<span class="header-btn-label"
            style="font-size:10px;color:var(--text3);font-family:'DM Sans',sans-serif;">More</span>
      </button>
      <input type="file" id="import-file-picker" accept=".json,application/json,text/plain,*/*"
          style="display:none" onchange="importData(event)">
    </div>
  </div>
  <!-- Row 2: Tonight's Venue (left, tap to set) + sync dot + version (right). The
       venue TEXT carries the tap handler (not the whole row), so tapping
       sync/version doesn't navigate to the venue picker. -->
  <div class="header-row-venue" id="header-row-venue">
    <!-- Venue chip (2026-06-06) — was inline text + a comma "/ clear" link that
         read as prose, not a control. Now a proper pill button with a chevron so
         the affordance is unmistakable. Clearing happens inside the Set Venue
         sheet (which already carries that action) — no inline clear here. -->
    <button id="header-venue-text" class="header-venue-chip header-venue-empty"
        onclick="_headerVenueRowClick(event)"
        title="Tap to set tonight's venue"><span class="hvc-pin">📍</span><span class="hvc-name">Set tonight's venue</span><span class="hvc-cv">⌄</span></button>
    <div class="header-right-cluster">
      <span id="sync-indicator" onclick="_showSyncStatusToast()" title="Sync status" style="display:none;"></span>
      <!-- Version badge is OWNER-ONLY (deploy-verify tool, not user noise). Hidden
           by default; revealed only when admin.js resolveEditMode() confirms the
           owner account (bar_name='u-street'), independent of ?edit=1. A cached
           kk_owner flag reveals it instantly on the owner's own device. -->
      <button id="header-version-btn" onclick="checkForUpdate()"
          title="Hard refresh to get the latest version" style="display:none;"><span class="hv-version">v4.99885 </span>↺</button>
    </div>
  </div>
</header>

<!-- Bottom tab bar — 6 primary DESTINATIONS anchored at the BOTTOM (thumb-reachable).
     2026-06-07: Vox Gym + Stats were promoted from the "More" overflow back to
     first-class tabs — they're two of BAR's defining features (the voice-training
     differentiator + the longitudinal-progress moat), and burying signature
     features in "More" tanks their discovery. Only UTILITIES (Settings + Help) stay
     in "More", which moved to a header ☰ button (config belongs up top, not in a
     thumb-tab). All 6 labels are short enough to fit at every real phone width
     (measured: "Songbook" 47px, "Vox Gym" 42px — both no-wrap down to 320px), so no
     responsive label swap is needed. The bar's flex:1 children auto-distribute, so
     6 needs no grid change. (2026-06-06 Apple-fy origin: replaced a 2-row TOP nav.) -->
<nav class="tab-bar">
  <button class="tab-btn active" onclick="showTab('songs')">
    <span class="tab-icon">📖</span>
    <span class="tab-label">Songbook</span>
  </button>
  <button class="tab-btn" onclick="showTab('night')" style="position:relative;">
    <span class="tab-icon">🎤</span>
    <span id="perform-badge" style="display:none;position:absolute;top:2px;right:5px;min-width:16px;height:16px;border-radius:8px;background:var(--accent);color:#fff;font-size:10px;font-weight:700;line-height:16px;text-align:center;padding:0 3px;box-sizing:border-box;pointer-events:none;"></span>
    <span class="tab-label">Sing</span>
  </button>
  <button class="tab-btn" onclick="showTab('voxgym')">
    <span class="tab-icon">💪</span>
    <span class="tab-label">Vox Gym</span>
  </button>
  <button class="tab-btn" onclick="showTab('people')" style="position:relative;">
    <span class="tab-icon">👥</span>
    <span id="friends-badge" style="display:none;position:absolute;top:2px;right:5px;min-width:16px;height:16px;border-radius:8px;background:var(--accent);color:#fff;font-size:10px;font-weight:700;line-height:16px;text-align:center;padding:0 3px;box-sizing:border-box;pointer-events:none;"></span>
    <span class="tab-label">Friends</span>
  </button>
  <button class="tab-btn" onclick="showTab('venues')">
    <span class="tab-icon">📍</span>
    <span class="tab-label">Venues</span>
  </button>
  <button class="tab-btn" onclick="showTab('stats')">
    <span class="tab-icon">📊</span>
    <span class="tab-label">Stats</span>
  </button>
</nav>

<main class="main">

  <!-- GUEST BANNER -->
  <div id="guest-banner" style="margin-bottom:2rem;background:rgba(124,58,237,0.07);border-bottom:1px solid rgba(124,58,237,0.18);padding:14px 16px 16px;">
    <div style="display:flex;align-items:flex-start;gap:10px;margin-bottom:10px;">
      <span style="font-size:18px;flex-shrink:0;margin-top:1px;">🔒</span>
      <div style="flex:1;min-width:0;">
        <div style="font-size:12px;font-weight:700;color:var(--text2);line-height:1.3;">Guest Mode — your data is saved on this device only</div>
        <div style="font-size:11px;color:var(--text3);margin-top:3px;line-height:1.5;">Sign up free to back up your songs, venues &amp; progress to the cloud.</div>
      </div>
      <button onclick="document.getElementById('guest-banner').style.display='none'" style="background:none;border:none;color:var(--text3);font-size:18px;cursor:pointer;flex-shrink:0;line-height:1;padding:0;" title="Dismiss">✕</button>
    </div>
    <button class="btn btn-primary" onclick="openAuthModal('password')" style="width:100%;justify-content:center;">Sign Up / Log In</button>
  </div>

  <!-- SONGBOOK TAB (unified Repertoire + Workshop) -->
  <div id="tab-songs" class="panel active" style="margin-top:-16px;">
    <!-- ░░ STICKY FILTER ZONE ░░ sticks below tab-bar while song list scrolls -->
    <div id="songbook-filter-zone" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:6px 16px 10px;border-bottom:1px solid var(--border);">

      <!-- Text search + inline Sort & Filter icon. The icon opens
           #songbook-filter-modal, which now also hosts Sort (2026-06-09
           consolidation — the standalone Sort select + Tag Filters button row
           was folded in here). -->
      <div style="position:relative;margin-bottom:6px;">
        <input class="search-input" type="text" placeholder="Search/Filter" oninput="renderSongs()" id="song-search" style="width:100%;display:block;box-sizing:border-box;padding-right:46px;">
        <button id="songbook-filter-btn" onclick="toggleTagFilterDrawer()" aria-label="Sort & filter" title="Sort & filter"
          style="position:absolute;right:5px;top:50%;transform:translateY(-50%);background:none;border:none;padding:6px;margin:0;cursor:pointer;color:var(--text2);display:flex;align-items:center;justify-content:center;">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="3" y1="7" x2="21" y2="7"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="17" x2="21" y2="17"/><circle cx="8" cy="7" r="2.4" fill="var(--bg)"/><circle cx="16" cy="12" r="2.4" fill="var(--bg)"/><circle cx="8" cy="17" r="2.4" fill="var(--bg)"/></svg>
          <span id="songbook-filter-badge" style="display:none;position:absolute;top:-2px;right:-2px;min-width:15px;height:15px;padding:0 3px;border-radius:8px;background:var(--accent);color:#fff;font-size:9px;font-weight:700;align-items:center;justify-content:center;box-sizing:border-box;line-height:1;"></span>
        </button>
      </div>

      <!-- Repertoire / Workshop buckets -->
      <div class="source-toggle-row">
        <button class="source-toggle rep on" id="sb-rep-btn" onclick="toggleSongbookFilter('repertoire')">
          🎵 Repertoire <span class="count" id="sb-rep-count"></span>
        </button>
        <button class="source-toggle work on" id="sb-work-btn" onclick="toggleSongbookFilter('working')">
          🔧 Workshop <span class="count" id="sb-work-count"></span>
        </button>
      </div>

      <!-- Persistent active-filter chain — visible whenever filters are
           active. Populated by renderActiveFilterChainSummary(). The in-page
           tag-filter drawer was retired — filters now live in
           #songbook-filter-modal, opened by the "+ Tag Filters" button. -->
      <div id="tag-filter-active-chain" style="display:none;"></div>

    </div><!-- end sticky filter zone (songbook) -->

    <!-- First-session orientation card. Populated by _renderOrientCardInto
         on app load. Empty until then; auto-hides via display:none when the
         user has dismissed it (kk_orient_songbook_dismissed). Restore via
         Settings → Start → "💡 Show orientation tips". -->
    <div id="orient-songbook-container"></div>

    <!-- Divider + View Songbook title + Add New Songs -->
    <div style="border-top:1px solid var(--border);margin:10px 0 8px;"></div>
    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
      <div style="display:flex;align-items:baseline;gap:7px;">
        <div style="font-family:'Bebas Neue',sans-serif;font-size:16px;letter-spacing:1px;color:var(--text2);">View Songbook</div>
        <span id="song-filter-count" style="font-size:11px;color:var(--text3);display:none;"></span>
      </div>
      <div style="display:flex;gap:6px;align-items:center;flex-shrink:0;">
        <!-- ✨ Suggest — the relocated Recommend tool (2026-06-07). AI suggests
             songs to ADD, based on your taste. Purple = AI, distinct from the
             red ＋ Add New. -->
        <button class="btn btn-sm" onclick="openSuggestSongs()" title="AI suggests songs to add, based on your taste"
          style="font-size:11px;padding:5px 9px;border-radius:14px;background:rgba(124,58,237,0.1);border:1px solid rgba(124,58,237,0.4);color:var(--accent3);font-weight:600;">✨ Suggest</button>
        <button class="btn btn-primary btn-sm" onclick="openAddSong()">＋ Add New</button>
      </div>
    </div>
    <div id="songbook-listhead" style="display:flex;align-items:center;gap:8px;padding:5px 10px;border-bottom:2px solid var(--border);font-size:11px;font-weight:600;color:var(--text3);text-transform:uppercase;letter-spacing:0.5px;">
      <span style="width:18px;flex-shrink:0;"></span>
      <span id="songbook-listhead-label" style="flex:1;">Artist / Title</span>
      <!-- List ↔ Gallery view toggle (2026-06-09). Gallery = album-art grid. -->
      <div class="sb-view-toggle" style="margin-left:auto;">
        <button id="sb-view-list" class="sb-view-btn active" onclick="setSongbookView('list')" aria-label="List view" title="List view"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg></button>
        <button id="sb-view-grid" class="sb-view-btn" onclick="setSongbookView('gallery')" aria-label="Gallery view" title="Gallery view"><svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="3" y="3" width="7.5" height="7.5" rx="1.6"/><rect x="13.5" y="3" width="7.5" height="7.5" rx="1.6"/><rect x="3" y="13.5" width="7.5" height="7.5" rx="1.6"/><rect x="13.5" y="13.5" width="7.5" height="7.5" rx="1.6"/></svg></button>
      </div>
    </div>
    <!-- Persistent help bar (top + bottom) removed 2026-05-31 — the gesture
         instructions it carried now live in the orientation card above
         (#orient-songbook-container), which has better hierarchy and a
         user-controllable dismiss state. -->
    <div class="song-list" id="song-list"></div>
    <!-- A–Z jump rail (fixed to the viewport right edge) — populated by
         _renderSongbookRail() from renderSongs(); shown only for the
         alphabetical sorts. Inside #tab-songs so a display:none on the tab
         auto-hides this position:fixed element. Ports the picker scrubber. -->
    <div id="songbook-rail" class="sb-rail" style="display:none;" aria-hidden="true"></div>
  </div>

  <!-- VOX GYM TAB -->
  <div id="tab-voxgym" class="panel" style="margin-top:-16px;">

    <!-- ── VOICE GYM SUB-NAV ────────────────────────────────────────────── -->
    <div id="vg-subnav" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:8px 16px 10px;border-bottom:1px solid var(--border);">
      <div id="vgp-edit-mode-indicator" style="display:none;align-items:center;gap:6px;padding:4px 0 6px;border-bottom:1px solid rgba(109,131,242,0.25);margin-bottom:6px;flex-wrap:wrap;">
        <span style="font-size:9px;font-weight:900;letter-spacing:2px;text-transform:uppercase;background:rgba(109,131,242,0.18);color:#6d83f2;padding:3px 8px;border-radius:6px;border:1px solid rgba(109,131,242,0.4);">✏️ EDIT MODE ACTIVE</span>
        <span style="font-size:10px;color:var(--text3);">Changes save locally until exported</span>
        <!-- Jump to the legacy #tab-artists panel where the batch AI taggers
             live (solo entries + 2026-05-23 group-member tagger). That panel
             isn't reachable from any top-nav, so this link is the only way
             into it without a console workaround. -->
        <button onclick="showTab('artists')" style="margin-left:auto;font-size:10px;font-weight:700;background:rgba(168,85,247,0.12);border:1px solid rgba(168,85,247,0.4);color:#c084fc;padding:4px 10px;border-radius:6px;cursor:pointer;">🛠 Batch Taggers →</button>
      </div>
      <!-- 6 → 3 (2026-06-09): Library (the 4 reading/reference pages, hub-and-spoke)
           · Practice (drills + Pitch Keyboard) · My Voice (recorder + memos + future
           paid voice profile). data-vg-top lets showVgPage derive the active pill
           from any page's group, so deep-links into a Library spoke light Library. -->
      <div style="display:flex;gap:6px;">
        <button class="vg-nav-btn active" data-vg-top="library" onclick="showVgPage('library',this)"
          style="flex:1;">📚 Library</button>
        <button class="vg-nav-btn" data-vg-top="practice" onclick="showVgPage('practice',this)"
          style="flex:1;">🎹 Practice</button>
        <button class="vg-nav-btn" data-vg-top="memos" onclick="showVgPage('memos',this)"
          style="flex:1;">🎙️ My Voice</button>
      </div>
    </div>

    <!-- First-session orientation card. Populated by _renderOrientCardInto
         on app load. Empty until then; auto-hides via display:none when the
         user has dismissed it (kk_orient_voxgym_dismissed). Restore via
         Settings → Start → "💡 Show orientation tips". Sits above the
         viewing window so it's visible regardless of which Vox Gym
         sub-page is active. -->
    <div id="orient-voxgym-container"></div>

    <!-- ── VIEWING WINDOW ───────────────────────────────────────────────── -->
    <div id="vg-window" style="padding-top:16px;">

      <!-- "‹ Library" breadcrumb — shown by showVgPage only when inside a Library
           spoke (Technique / Famous Voices / Anatomy / Foundations), hidden on the
           index + on Practice / My Voice. Returns to the Library hub. -->
      <button id="vg-lib-breadcrumb" onclick="showVgPage('library')" style="display:none;">‹ Library</button>

      <!-- PAGE: LIBRARY (hub-and-spoke index — the Vox Gym default landing) -->
      <div id="vgp-library" class="vg-page">
        <div class="vg-lib-menu">
          <button class="vg-lib-row" onclick="showVgPage('technique')">
            <span class="vg-lib-ico">📖</span>
            <span class="vg-lib-txt"><span class="vg-lib-title">Vocal Techniques</span><span class="vg-lib-sub">36 techniques — register, resonance, texture &amp; phrasing</span></span>
            <span class="vg-lib-chev">›</span>
          </button>
          <button class="vg-lib-row" onclick="showVgPage('profiles')">
            <span class="vg-lib-ico">🎙️</span>
            <span class="vg-lib-txt"><span class="vg-lib-title">Famous Voices</span><span class="vg-lib-sub">800+ artist voice profiles — see how the pros do it</span></span>
            <span class="vg-lib-chev">›</span>
          </button>
          <button class="vg-lib-row" onclick="showVgPage('anatomy')">
            <span class="vg-lib-ico">🫁</span>
            <span class="vg-lib-txt"><span class="vg-lib-title">Anatomy of the Voice</span><span class="vg-lib-sub">How your instrument actually works</span></span>
            <span class="vg-lib-chev">›</span>
          </button>
          <button class="vg-lib-row" onclick="showVgPage('foundations')">
            <span class="vg-lib-ico">🏗</span>
            <span class="vg-lib-txt"><span class="vg-lib-title">Foundations</span><span class="vg-lib-sub">Start here — the building blocks of good singing</span></span>
            <span class="vg-lib-chev">›</span>
          </button>
        </div>
      </div>

      <!-- PAGE: FOUNDATIONS -->
      <div id="vgp-foundations" class="vg-page vgp-found" style="display:none;">
        <div class="vg-toc-layout">
          <div class="vg-toc-pane" id="vgtoc-foundations">
            <div class="vg-toc-inner">
              <span class="vg-toc-section-label">Foundations</span>
              <a class="vg-toc-link" onclick="vgTocGo('found-stack')">Overview</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-posture')">L1 · Posture</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-breath')">L2 · Breath</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-closure')">L3 · Cord Closure</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-onset')">L3 · Onset Types</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-passaggio')">L4 · Passaggio</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-registers')">L5 · Registers</a>
              <a class="vg-toc-link" onclick="vgTocGo('found-next')">Next Steps</a>
            </div>
          </div>
          <div class="vg-toc-content">
        <div class="content">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-stack">
    <div class="section-kicker">Overview</div>
    <div class="section-title">The Foundation Stack</div>
    <div class="section-body">
      Every advanced vocal technique — grit, falsetto, belt, vibrato, compression, breath-smoke — is a <strong>modification applied on top of a working base system</strong>. If the base is broken, the technique breaks too. A singer with poor breath support who adds grit gets strain. A singer who can't navigate their passaggio who tries to belt gets a shout. The techniques in the Voice Technique Guide assume this foundation exists.
      <br><br>
      The stack below is ordered. Each layer depends on the one beneath it. You can work on multiple layers at once — and you'll often find fixing a lower layer mysteriously resolves problems you thought lived higher up.
    </div>

    <div class="stack">
      <div class="stack-layer foundation">
        <div class="stack-num">1</div>
        <div class="stack-body">
          <div class="stack-tag">Foundation Layer 1</div>
          <div class="stack-name">Posture &amp; Body Setup</div>
          <div class="stack-desc">The physical frame everything else happens inside. Alignment, released tension, open throat.</div>
        </div>
      </div>
      <div class="stack-layer foundation">
        <div class="stack-num">2</div>
        <div class="stack-body">
          <div class="stack-tag">Foundation Layer 2</div>
          <div class="stack-name">Breath Mechanics &amp; Support</div>
          <div class="stack-desc">Diaphragmatic breathing, appoggio engagement, controlled subglottal pressure.</div>
        </div>
      </div>
      <div class="stack-layer foundation">
        <div class="stack-num">3</div>
        <div class="stack-body">
          <div class="stack-tag">Foundation Layer 3</div>
          <div class="stack-name">Cord Closure &amp; Onset</div>
          <div class="stack-desc">Efficient glottal closure — the folds meeting cleanly without pressing or leaking. Clean attack with no breath waste.</div>
        </div>
      </div>
      <div class="stack-layer foundation">
        <div class="stack-num">4</div>
        <div class="stack-body">
          <div class="stack-tag">Foundation Layer 4</div>
          <div class="stack-name">Passaggio Navigation</div>
          <div class="stack-desc">Passing through the register break without flipping, cracking, or forcing. The gateway to full range.</div>
        </div>
      </div>
      <div class="stack-layer foundation">
        <div class="stack-num">5</div>
        <div class="stack-body">
          <div class="stack-tag">Foundation Layer 5</div>
          <div class="stack-name">Register Development</div>
          <div class="stack-desc">Building a reliable chest voice, mix, and head voice — and connecting them into a seamless range.</div>
        </div>
      </div>
      <div class="stack-layer advanced">
        <div class="stack-num">↑</div>
        <div class="stack-body">
          <div class="stack-tag">Advanced Layer — Voice Technique Guide</div>
          <div class="stack-name">Texture, Resonance &amp; Colour</div>
          <div class="stack-desc">Grit, growl, compression, vibrato, breath-smoke, falsetto, belt, covered voice — all the tags in the Technique Guide live here. They are modifications applied on top of a working foundation.</div>
        </div>
      </div>
    </div>

    <div class="note-box green">
      <strong>How to use this guide:</strong> Work through the sections in order the first time. After that, treat it as a diagnostic — if something in your upper register sounds wrong, the problem is almost always in Layer 2, 3, or 4, not in the technique you were trying to execute.
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-posture">
    <div class="section-kicker">Foundation Layer 1</div>
    <div class="section-title">Posture &amp; Body Setup</div>
    <div class="section-body">
      The voice lives inside a body. The shape of that body — how it's aligned, where it holds tension, how the ribs and throat are positioned — directly determines how much the vocal instrument can do. Good singing posture is not about looking correct; it's about removing the physical obstacles that prevent the instrument from working.
    </div>

    <!-- GEMINI_VISUAL id="visual-foundation-posture"
         SUBJECT: Two-panel comparison: collapsed vs. aligned singing posture with anatomical callouts
         SHOW TWO PANELS, side by side, sagittal (side) view silhouette of a standing singer:
           Panel A "Collapsed / Tension-loaded":
             — Head jutted forward (forward head posture)
             — Shoulders rounded inward
             — Chest sunken, ribs compressed
             — Neck muscles visibly tense / highlighted red
             — Larynx displaced upward due to neck tension
             — Lumbar spine compressed / over-arched
             — Labels: "Forward head posture", "Neck tension → larynx displacement",
               "Compressed ribs → reduced lung capacity", "Collapsed chest"
           Panel B "Aligned / Released":
             — Head stacked over shoulders, ears over hips
             — Shoulders back and relaxed, not forced
             — Chest gently lifted — ribs expanded naturally
             — Neck long and released — larynx in neutral position
             — Spine in natural S-curve, not exaggerated
             — Feet hip-width apart, weight distributed
             — Labels: "Head stacked — neutral larynx", "Long neck — released throat",
               "Lifted chest — ribs free to expand", "Natural spinal curve"
         STYLE: Dark background (--bg: #0a0a0f). Clean anatomical illustration.
           Tension points in red/orange in Panel A. Alignment lines in soft green/emerald in Panel B.
           Silhouette fill warm skin-tone. Side-profile line art style, semi-realistic.
         SIZE: approximately 640px wide × 420px tall (two-panel horizontal) -->
    <div class="visual-block" id="visual-foundation-posture">
      <img src="/images/visual-foundation-posture.png" alt="visual-foundation-posture" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 1 — Collapsed vs. aligned posture. The body is the instrument's housing — tension and misalignment are physical resistance the voice has to work against.</div>

    <div class="subsection">
      <div class="subsection-title">The setup</div>
      <div class="subsection-body">
        Stand with feet roughly hip-width apart. Let the weight drop down through your legs into the floor — don't lock the knees. Stack the head gently over the shoulders so the ears sit above the hips. Lift the sternum slightly — not a military chest-out, just enough to open the ribs. Let the shoulders settle back and down without forcing them there. The neck should feel long and free. This is not an effortful position; if it feels like work to hold, you're doing too much.
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">The throat</div>
      <div class="subsection-body">
        The single most destructive form of tension in singing is <strong>extrinsic laryngeal tension</strong> — the muscles around the outside of the larynx gripping and pulling it up or in. You can feel this: if your jaw, under-chin, or the sides of your neck tighten noticeably as you go higher, you have extrinsic tension. It limits range, creates strain, and is the root cause of most "my throat closes up" problems. The fix is not trying to relax during singing — it's learning what released feels like during low-stakes vocalisation first, so the muscle memory exists to draw from.
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">The jaw and tongue</div>
      <div class="subsection-body">
        The jaw should drop open on its hinge — not pushed forward or pulled back — with the tongue flat and wide in the mouth rather than bunching at the back. A bunched tongue narrows the pharyngeal space and raises the larynx. Practice singing with two fingers between your front teeth occasionally to train jaw release. The tongue should feel heavy and spread, not pointed or retracted.
      </div>
    </div>

    <div class="note-box amber">
      <strong>Diagnostic test:</strong> Put two fingers lightly against the outside of your larynx (Adam's apple area) and sing up through your upper range. If you feel it shoot upward more than a centimetre or two, or if the muscles around it tighten under your fingers, extrinsic tension is present and limiting your range before any technique issue comes into play.
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-breath">
    <div class="section-kicker">Foundation Layer 2</div>
    <div class="section-title">Breath Mechanics &amp; Support</div>
    <div class="section-body">
      "Support your voice" is the most-given and least-explained instruction in singing. What it actually means mechanically: <strong>controlled management of subglottal air pressure</strong> — keeping the pressure below the folds steady across a phrase, rather than dumping it all at once or letting it collapse. The folds need consistent, calibrated pressure to vibrate efficiently. Too little and the tone is breathy and unsupported. Too much and the folds strain under excess pressure.
    </div>

    <!-- GEMINI_VISUAL id="visual-foundation-breath"
         SUBJECT: Breath support anatomy — three-state diagram showing inhalation, appoggio, and collapse
         SHOW THREE PANELS (front-view torso), left to right:
           Panel A "Inhalation":
             — Diaphragm dome descends (flattens downward) — arrow pointing down
             — Ribs expand outward on all sides — arrows pointing outward
             — Lungs shown filled (large, light pink)
             — Abdominals relaxed / released outward slightly
             — Labels: "Diaphragm descends", "Ribs expand", "Lungs fill"
           Panel B "Appoggio (Supported Singing)":
             — Diaphragm held in descended position — label "Held"
             — External intercostals shown engaged, keeping ribs from collapsing inward — label "Ribs held open"
             — Abdominals gently engaged providing upward pressure — label "Abs: controlled pressure"
             — Arrow showing steady, controlled airflow upward through trachea to larynx
             — Labels: "Appoggio — rib cage held open against natural recoil", "Steady subglottal pressure"
           Panel C "Chest Collapse (No Support)":
             — Ribs collapsed inward suddenly — arrows inward, red
             — Diaphragm bouncing up — red arrow upward
             — Burst of air shooting up trachea uncontrolled — red arrow, label "Pressure dump"
             — Labels: "Ribs collapse", "Diaphragm rebounds", "Uncontrolled pressure surge → strain"
         MUSCLES SHOWN: Diaphragm (dome, below lungs), External intercostals (between ribs),
           Rectus abdominis (front abdominal wall), Trachea with directional airflow arrows.
         STYLE: Dark background. Three-panel horizontal layout.
           Warm anatomical palette: muscle groups in different warm colours (diaphragm = coral/salmon,
           intercostals = orange-tan, abdominals = amber). Trachea and airflow in blue/teal.
           Red highlights for Panel C tension/collapse points. Clear labels throughout.
         SIZE: approximately 720px wide × 400px tall -->
    <div class="visual-block" id="visual-foundation-breath">
      <img src="/images/visual-foundation-breath.png" alt="visual-foundation-breath" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 2 — Breath support states. Appoggio (centre) is the goal: the rib cage is held open against natural recoil, creating a controlled, steady pressure rather than a dump or a collapse.</div>

    <div class="subsection">
      <div class="subsection-title">Diaphragmatic breathing</div>
      <div class="subsection-body">
        The diaphragm is a dome-shaped muscle sitting below the lungs. When it contracts, it flattens downward, creating negative pressure that pulls air into the lungs. A deep breath should cause the belly to push outward (the descending diaphragm pushes the abdominal organs down and forward) rather than the chest and shoulders rising. Chest/shoulder breathing uses the secondary respiratory muscles — it's shallow, inefficient, and tires the neck muscles you need to keep relaxed for singing. <strong>The test:</strong> lie flat on your back and notice how you breathe naturally — the belly rises and falls, the chest stays relatively still. That's the mechanics you're after when standing.
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">Appoggio — the classical solution</div>
      <div class="subsection-body">
        <strong>Appoggio</strong> (Italian: "to lean") is the classical technique for breath support. After inhaling, you maintain the expanded rib-cage position of inhalation while singing — the intercostal muscles resist the natural elastic recoil of the ribs closing in. This creates a slow, controlled release of air pressure rather than a sudden dump. The abdominals engage gradually from below to provide steady upward pressure. The result: consistent subglottal pressure across the length of a phrase. Singers describe it as "singing on the breath" or "keeping the space." Physiologically, it's a managed tug-of-war between the expanding muscles (intercostals holding ribs out) and the collapsing tendency of the chest.
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">Practical exercises</div>
      <div class="subsection-body">
        <strong>1. Hiss exercise:</strong> Take a full breath into the belly, then release it on a slow "sss" hiss — try to make it last 20, then 30, then 40 seconds. The challenge forces your intercostals and abdominals to engage without you consciously thinking about them. You'll feel the sides of the ribcage working.
        <br><br>
        <strong>2. Staccato scales:</strong> Sing short, punchy staccato notes on a vowel. Each note requires a small abdominal engagement on the attack. This trains the abdomen to be the initiator of each note's pressure rather than the throat.
        <br><br>
        <strong>3. Phrase extension:</strong> Sing a phrase you know, then repeat it trying to end with more air in reserve than last time. The goal is not to conserve air by singing quietly — it's to support efficiently so less air wastes per note.
      </div>
    </div>

    <div class="note-box green">
      <strong>Key diagnostic:</strong> If your tone gets airier or weaker at the end of phrases — or if you run out of breath on phrases that seem short — the problem is almost always in Layer 2, not Layer 3 or above. Fix the breath first.
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-closure">
    <div class="section-kicker">Foundation Layer 3</div>
    <div class="section-title">Cord Closure</div>
    <div class="section-body">
      The vocal folds need to meet cleanly along their full length with each vibration cycle. <strong>Adduction</strong> is the closing of the folds; <strong>abduction</strong> is their opening. Good cord closure means the folds adduct enough to vibrate efficiently without pressing hard against each other. Too little closure = breathy, thin tone. Too much closure = pressed, strained, fatiguing tone. The goal is balanced adduction — sometimes called <strong>cord approximation</strong> or a <strong>clean glottal closure</strong>.
    </div>

    <!-- GEMINI_VISUAL id="visual-foundation-closure"
         SUBJECT: Three-panel top-down view of vocal folds showing closure spectrum
         SHOW THREE PANELS, all top-down (laryngoscope) view looking down into the larynx:
           Panel A "Under-adducted (Breathy)":
             — Folds clearly separated even at closest point during vibration
             — Visible gap (glottal chink) running most of the fold length
             — Air leak shown as soft blue haze passing through
             — Label: "Glottal chink", "Air leak", "Breathy / unsupported"
           Panel B "Balanced Closure (Goal)":
             — Folds meeting cleanly along full length at closure phase
             — Slight definition between folds — not smashed together, cleanly approximated
             — Clean, efficient contact zone highlighted in soft green
             — Label: "Full-length closure", "Efficient vibration", "Clean, clear tone"
           Panel C "Over-adducted (Pressed / Glottal)":
             — Folds pressed hard together — muscular tension visible in surrounding tissue
             — Red highlight indicating strain/compression
             — Label: "Pressed closure", "Excess tension", "Strained / fatiguing"
         STYLE: Top-down laryngoscope perspective.
           Folds: warm pink/salmon. Cartilage structures: muted blue-grey.
           Panel A: cool blue haze for air leak. Panel B: clean, neutral. Panel C: red tension markers.
           Dark background. Consistent lighting across panels.
         SIZE: approximately 640px wide × 280px tall (three-panel horizontal) -->
    <div class="visual-block" id="visual-foundation-closure">
      <img src="/images/visual-foundation-closure.png" alt="visual-foundation-closure" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 3 — Cord closure spectrum. Balanced adduction (centre) produces efficient vibration. Both extremes — too little or too much — create problems that technique cannot fix from above.</div>

    <div class="subsection">
      <div class="subsection-title">Training closure without pressing</div>
      <div class="subsection-body">
        The best exercises for cord closure are ones that encourage the folds to meet firmly but without the extrinsic muscle tension that causes pressing. <strong>Lip trills / lip bubbles</strong> are effective: the back-pressure created by the buzzing lips regulates subglottal pressure and forces the folds to find their own natural closure rather than being squeezed by external effort. <strong>Sirens on "ng"</strong> (the sound in "singing") also work well — the ng position keeps the throat open while the folds close cleanly for the tone. <strong>Straw phonation</strong> (singing through a thin straw into water) provides similar back-pressure regulation.
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">The breathiness problem</div>
      <div class="subsection-body">
        Many singers — especially those who developed voice in a pop/indie context — have a habitual glottal chink: a small gap at the back of the folds that never fully closes. This reads as a permanent breathy quality and limits power, projection, and the ability to sustain tone. It is not a character choice when it's the only option available. Training clean closure gives you access to both clean and deliberately breathy sounds (as in <span class="tag">TEX-breath-smoke</span>) — the difference being that one is a choice and one is a default you can't escape.
      </div>
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-onset">
    <div class="section-kicker">Foundation Layer 3 (cont.)</div>
    <div class="section-title">Onset Types</div>
    <div class="section-body">
      The onset is how a note begins — specifically, the relationship between the start of airflow and the start of fold vibration. It's one of the clearest diagnostic windows into the health of a singer's technique. There are three types.
    </div>

    <div class="onset-grid">
      <div class="onset-card">
        <div class="onset-icon">💨</div>
        <div class="onset-name">Aspirate Onset</div>
        <div class="onset-desc">Air flows before the folds close — you hear a "h-" before the note. Breathy attack, wasted air, folds close late. Common in untrained and folk/indie singers as a default.</div>
        <div class="onset-verdict caution">Caution — air waste</div>
      </div>
      <div class="onset-card">
        <div class="onset-icon">✅</div>
        <div class="onset-name">Simultaneous Onset</div>
        <div class="onset-desc">Air and fold vibration start at exactly the same moment. Clean, clear, efficient. No wasted air, no glottal click. This is the goal for most singing contexts.</div>
        <div class="onset-verdict good">Goal</div>
      </div>
      <div class="onset-card">
        <div class="onset-icon">⚡</div>
        <div class="onset-name">Glottal Onset</div>
        <div class="onset-desc">Folds close fully before airflow — air builds pressure then blows them apart with a hard click or pop. Stresses the folds. Sometimes used deliberately for percussive effect, but damaging as a default.</div>
        <div class="onset-verdict bad">Avoid as default</div>
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">Training the simultaneous onset</div>
      <div class="subsection-body">
        The most direct way to train simultaneous onset is singing through an "h" intentionally, then gradually removing it while keeping the airflow timing the same. Start with "hah… hah… hah…" — clean, even, no click — then reduce the h to a breath-onset, then to nothing. The note should start with the same quality of airflow and closure. Another route: singing on a slow scale starting with "mmm-ah" — the nasal resonance on the "m" establishes fold vibration before the vowel opens, training the folds to be active and adducted before the onset demand arrives.
      </div>
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-passaggio">
    <div class="section-kicker">Foundation Layer 4</div>
    <div class="section-title">Passaggio Navigation</div>
    <div class="section-body">
      The <strong>passaggio</strong> (Italian: "passage") is the register break — the zone in the pitch range where the laryngeal mechanism wants to switch from the TA-dominant chest-voice configuration to the CT-dominant head-voice configuration. It is not a flaw to be hidden; it is a structural feature of the instrument that every singer must learn to navigate. The quality of a singer's passaggio navigation is often the single biggest determinant of their effective range.
    </div>

    <!-- GEMINI_VISUAL id="visual-foundation-passaggio"
         SUBJECT: Diagram of passaggio navigation strategies — pitch range on horizontal axis
         SHOW: A horizontal pitch range bar spanning low (left, bass) to high (right, soprano/tenor)
           — Chest Voice zone on left in warm amber/orange
           — Passaggio zone marked in centre as a highlighted band — label "Passaggio (Register Break)"
             with approximate pitch ranges noted: "Men: approx E4–G4 / Women: approx A4–C5"
           — Head Voice / Upper Register zone on right in cool blue-purple
           BELOW THE BAR: three strategy arrows / paths passing through the passaggio zone:
             Strategy A — "Forcing / Driving chest" — arrow ploughs straight through passaggio with
               label "Chest driven above break → strain, tension, ceiling" — colour red/orange
             Strategy B — "Flipping / Avoiding" — arrow drops steeply into falsetto just before passaggio
               with label "Flip — gap in the voice, two disconnected registers" — colour yellow/amber
             Strategy C — "Mix / Blended passage" — smooth curve through passaggio, gradual CT-TA
               handoff, label "Mix — smooth CT/TA transition, seamless range" — colour green
           ADDITIONAL LABELS: TA dominant (left), CT dominant (right), "Break zone" with dashed border
         STYLE: Dark background. Clean diagrammatic. Horizontal range bar as main visual spine.
           Warm colours for chest, cool for head, green for mix path. Clear arrows and labels.
         SIZE: approximately 660px wide × 360px tall -->
    <div class="visual-block" id="visual-foundation-passaggio">
      <img src="/images/visual-foundation-passaggio.png" alt="visual-foundation-passaggio" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 4 — Three navigation strategies through the passaggio. Only the mix/blend path produces a seamless, usable range — the other two produce either a wall or a gap.</div>

    <div class="subsection">
      <div class="subsection-title">What's actually happening</div>
      <div class="subsection-body">
        As pitch rises, the cricothyroid (CT) muscle increasingly takes over from the thyroarytenoid (TA). The CT lengthens and thins the folds to raise pitch. The TA resists this — it's the mass contributor, the chest-voice engine. In the passaggio zone, both muscles are competing for dominance. The flip or crack happens when the TA suddenly gives up rather than gradually releasing. The sensation of "breaking" is the TA releasing too abruptly. The goal is a <strong>graduated handoff</strong> — the CT progressively takes more authority while the TA releases slowly enough that the tone never goes hollow or suddenly flips.
      </div>
    </div>

    <div class="passaggio-ladder">
      <div class="pass-step danger">
        <div class="pass-icon">🔴</div>
        <div class="pass-body">
          <div class="pass-label">Forcing chest above the break</div>
          <div class="pass-body">Driving full TA-dominant chest voice above the passaggio. The CT is being overridden by sheer pressure. Eventually hits a ceiling — the voice cracks, the note disappears, or the singer strains. This is not belt (which is a coordinated yell-level system) — it's just driving the wrong mechanism past its range.</div>
        </div>
      </div>
      <div class="pass-step danger">
        <div class="pass-icon">🟡</div>
        <div class="pass-body">
          <div class="pass-label">The flip / abrupt register switch</div>
          <div class="pass-body">The TA releases all at once and the voice flips into falsetto (CT dominant, incomplete closure). Audible as a sudden change in quality — yodel-like, two separate voices. Not a mechanical failure but a coordination failure: the TA let go before the CT built enough tension to take over cleanly.</div>
        </div>
      </div>
      <div class="pass-step goal">
        <div class="pass-icon">🟢</div>
        <div class="pass-body">
          <div class="pass-label">Mix / blended passage — the goal</div>
          <div class="pass-body">The CT builds tension progressively while the TA releases gradually. Neither takes over suddenly. The tone stays consistent through the break — changing slightly in character (lighter, more focused) but not cracking or flipping. This is the mechanism behind "mixed voice," "voix mixte," and the operatic mezza voce.</div>
        </div>
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">Training the passage</div>
      <div class="subsection-body">
        <strong>Slow sirens:</strong> Slide slowly and smoothly from low to high on a gentle "ooh" or lip trill, aiming to pass through the break without cracking or flipping. Slow speed gives the muscles time to negotiate. If you flip, back up and approach more gradually.
        <br><br>
        <strong>Approaching from above:</strong> Start in head voice on a high note and slide down through the passaggio, rather than driving up from chest. This trains the CT to maintain its claim while the TA reactivates, rather than TA trying to fight its way up.
        <br><br>
        <strong>Vowel modification:</strong> Closed vowels like "oo" and "ee" naturally encourage mix — the vocal tract shape for these vowels discourages the full-mass chest configuration. Practising passages on these vowels first, then transferring to open vowels, helps encode the blended coordination.
      </div>
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-registers">
    <div class="section-kicker">Foundation Layer 5</div>
    <div class="section-title">Register Development</div>
    <div class="section-body">
      Once the passaggio can be navigated cleanly, the work becomes developing each register fully and then integrating them. Most singers have an uneven instrument — a strong chest voice and a weak or underdeveloped head voice, or vice versa. Strength and agility in all registers is what gives the advanced techniques somewhere to live.
    </div>

    <!-- GEMINI_VISUAL id="visual-foundation-registers"
         SUBJECT: Register development diagram — three registers with CT/TA muscle contribution shown
         SHOW: Three stacked horizontal sections representing pitch range, lowest to highest:
           Section 1 "Chest Voice (Modal / TA Dominant)":
             — Wide, full, warm colour (amber/orange) — representing full-mass fold vibration
             — Bar showing TA muscle = HIGH contribution, CT = LOW contribution
             — Fold illustration: thick, full contact across fold length
             — Labels: "Full fold mass", "TA dominant", "Rich, powerful, speech-like"
           Section 2 "Mix Voice (Blended — CT/TA both active)":
             — Medium width, medium colour (between amber and blue-purple, a warm purple or mauve)
             — Bar: TA = MEDIUM, CT = MEDIUM (equal or near-equal)
             — Fold illustration: thinner folds, still full closure
             — Labels: "Thinning folds, full closure", "CT/TA balanced", "Powerful but lighter"
           Section 3 "Head Voice (CT Dominant — full closure)":
             — Narrower, cool colour (blue-purple)
             — Bar: TA = LOW, CT = HIGH
             — Fold illustration: thin, elongated folds, clean full closure
             — Labels: "Thin, elongated folds", "CT dominant", "Ringing, sourceless quality"
           OPTIONAL INSET: Falsetto shown in dashed outline — same CT dominance as head voice
             but with a visible glottal gap — label "Falsetto: CT dominant, incomplete closure"
         STYLE: Clean diagrammatic. Dark background. Warm amber through cool purple gradient
           across register zones. Small fold cross-section illustrations in each zone.
           TA/CT contribution shown as small stacked bar charts or visual indicators.
         SIZE: approximately 600px wide × 480px tall (vertical stack) -->
    <div class="visual-block" id="visual-foundation-registers">
      <img src="/images/visual-foundation-registers.png" alt="visual-foundation-registers" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 5 — The three main registers and their muscle configurations. Developing each fully, then connecting them through the passaggio, produces a complete, even instrument.</div>

    <div class="mechanic-grid">
      <div class="mechanic-card">
        <div class="mechanic-card-title">Building Chest Voice</div>
        <div class="mechanic-card-tag">TA dominance · mass · power</div>
        <div class="mechanic-card-body">
          Chest voice needs <strong>resonance and projection</strong>, not volume for its own sake. Speak-sing exercises ("yelling" a note on a word you'd shout) connect you to the uninhibited chest mechanism. Slides from chest up toward the break and back down. Scales on "gah," "bah," open vowels with full engagement — not holding back. The risk area is driving too high in chest; the development area is widening the range and strengthening the lower-middle register.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Building Head Voice</div>
        <div class="mechanic-card-tag">CT dominance · thinning · ring</div>
        <div class="mechanic-card-body">
          Head voice is underdeveloped in most untrained singers because it requires trusting a configuration that feels unsupported. <strong>Starting high and coming down</strong> is more productive than fighting up to it. Falsetto-to-head-voice transitions (moving from incomplete to full closure in the CT-dominant mechanism) — the moment the airiness of falsetto snaps into the ring of full head voice closure is the sensation to chase. Humming in the upper register, "ng" scales above the break, hooty "ooh" descents.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Building Mix</div>
        <div class="mechanic-card-tag">CT/TA balance · handoff · blend</div>
        <div class="mechanic-card-body">
          Mix is not a third register — it's the coordinated <strong>overlap zone</strong> where both muscles are active. You cannot consciously operate the CT and TA individually. What you can do is adjust vocal tract shape, vowel, and onset quality to encourage one or the other. Brighter/more forward vowels pull toward chest weight; darker/more rounded vowels encourage thinning. Building mix is mostly about building the passaggio navigation (Layer 4) and applying it to more and more vowels and pitches.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Integration</div>
        <div class="mechanic-card-tag">full range · seamless · even</div>
        <div class="mechanic-card-body">
          A fully integrated voice has consistent tone quality across the range — not the same timbre (it will naturally change), but a consistent <strong>ease and resonance</strong> that doesn't have a "here's where I struggle" zone. The test: can you sing a slow scale from low chest to high head and back without any note feeling harder than the others, without any flip or audible gear-change? That evenness is the goal. Everything in the Voice Technique Guide runs on top of that.
        </div>
      </div>
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="found-next">
    <div class="section-kicker">Where to go next</div>
    <div class="section-title">You Have a Foundation. Now What?</div>
    <div class="section-body">
      Once you have consistent breath support, clean cord closure, a navigable passaggio, and developing registers, you're ready to start working on the technique layer. This is where the Voice Technique Guide and the Artist Profiles become practical tools rather than abstract references — because you now have an instrument capable of executing them.
    </div>

    <div class="note-box purple">
      <strong>The order of diagnosis:</strong> When something goes wrong in a technique — your grit sounds strangled, your falsetto won't sit, your belt cracks — work backwards through the stack before assuming it's a technique problem. Layer 2 (breath) accounts for probably 60% of technique failures. Layer 3 (closure/onset) for another 20%. Layer 4 (passaggio) for most of the rest. The technique itself is rarely the issue.
    </div>

    <div style="display:flex;flex-direction:column;gap:10px;margin-top:24px;">
      <a href="#" onclick="showVgPage('technique',document.getElementById('vgnav-technique'));return false;" style="display:flex;align-items:center;gap:12px;padding:14px 18px;border-radius:var(--radius);border:1px solid rgba(109,131,242,0.3);background:rgba(109,131,242,0.06);color:var(--text);text-decoration:none;">
        <span style="font-size:22px;">📖</span>
        <div>
          <div style="font-size:13px;font-weight:700;margin-bottom:2px;">Voice Technique Guide</div>
          <div style="font-size:11px;color:var(--text3);">All 26 technique tags with exemplars, diagnostics, and perceptual tests</div>
        </div>
        <span style="margin-left:auto;color:var(--text3);font-size:16px;">→</span>
      </a>
      <a href="#" onclick="showVgPage('anatomy',document.getElementById('vgnav-anatomy'));return false;" style="display:flex;align-items:center;gap:12px;padding:14px 18px;border-radius:var(--radius);border:1px solid rgba(232,57,90,0.3);background:rgba(232,57,90,0.04);color:var(--text);text-decoration:none;">
        <span style="font-size:22px;">🫁</span>
        <div>
          <div style="font-size:13px;font-weight:700;margin-bottom:2px;">Voice Anatomy &amp; Physiology</div>
          <div style="font-size:11px;color:var(--text3);">The biological underpinning — what's physically happening when you sing</div>
        </div>
        <span style="margin-left:auto;color:var(--text3);font-size:16px;">→</span>
      </a>
    </div>
  </section>

  <div class="note-box" style="margin-top:40px;">
    <strong>Disclaimer:</strong> The technique descriptions on this page represent pedagogical consensus and functional models derived from voice science and established teaching frameworks. They are not clinical findings. For rigorous scientific grounding: <strong>Complete Vocal Technique</strong> (Cathrine Sadolin, CVI) and <strong>The Estill Voice Model</strong> (Jo Estill / Kimberly Steinhauer) are the most rigorous style-independent frameworks. For academic voice science: <em>The Science of the Singing Voice</em> (Johan Sundberg) is the standard reference text.
  </div>

  <div style="font-size:10px;color:#3a3840;text-align:right;margin-top:24px;">
    Building A Rockstar Foundation · v1.0 · April 2026 · 5 diagrams (Gemini visual placeholders)
  </div>

</div>
          </div><!-- end vg-toc-content -->
        </div><!-- end vg-toc-layout -->
      </div>

      <!-- PAGE: ANATOMY -->
      <div id="vgp-anatomy" class="vg-page vgp-ap" style="display:none;">
        <div class="vg-toc-layout">
          <div class="vg-toc-pane" id="vgtoc-anatomy">
            <div class="vg-toc-inner">
              <span class="vg-toc-section-label">Anatomy</span>
              <a class="vg-toc-link" onclick="vgTocGo('ap-instrument')">The Instrument</a>
              <a class="vg-toc-link" onclick="vgTocGo('ap-signal-chain')">Signal Chain</a>
              <a class="vg-toc-link" onclick="vgTocGo('ap-registers')">Vocal Registers</a>
              <a class="vg-toc-link" onclick="vgTocGo('ap-resonance')">Resonance</a>
              <a class="vg-toc-link" onclick="vgTocGo('ap-passaggio')">The Passaggio</a>
              <a class="vg-toc-link" onclick="vgTocGo('ap-distortion')">Distortion</a>
              <a class="vg-toc-link" onclick="vgTocGo('ap-breath')">Breath Support</a>
            </div>
          </div>
          <div class="vg-toc-content">
        <div class="content">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-instrument">
    <div class="section-kicker">Section 1</div>
    <div class="section-title">The Instrument</div>
    <div class="section-body">
      The human voice is a wind instrument with a variable resonator. Unlike a guitar (fixed body, fixed strings) or a trumpet (fixed tube), every structural element of the vocal instrument — the vibrating source, the resonating chambers, the shape of the tract — is actively reconfigured in real time by the singer. This is what makes voice the most expressive instrument and the hardest to teach: there's nothing to look at.
    </div>

    <div class="subsection">
      <div class="subsection-title">The three-component model</div>
      <div class="subsection-body">
        Voice science divides the singing instrument into three components: <strong>the power source</strong> (lungs, diaphragm, breath pressure), <strong>the vibrator</strong> (vocal folds in the larynx), and <strong>the resonator</strong> (the entire vocal tract above the larynx — pharynx, mouth, nasal cavity). Each component operates semi-independently, and technique tags in Building A Rockstar correspond to specific configurations of the vibrator (Axis 2) and resonator (Axis 1).
      </div>
    </div>

    <!-- GEMINI_VISUAL id="visual-sagittal"
         SUBJECT: Sagittal (side-view) cross-section of head and neck showing the vocal instrument
         LABELS REQUIRED: Lungs, Diaphragm, Trachea, Larynx, Vocal Folds (inside larynx),
           Pharynx, Soft Palate, Hard Palate, Nasal Cavity, Oral Cavity (mouth),
           Lips, Tongue
         STYLE: Clean anatomical illustration. Dark background #0a0a0f.
           Structural fills in deep blue-grey (#1c2a3a). Label lines in #e8395a.
           Label text in #f0f0f8, small sans-serif. Accent the larynx region distinctly.
         SIZE: approximately 600px wide × 520px tall
         NOTE: This is the "master" anatomy diagram — the most important visual on the page -->
    <div class="visual-block" id="visual-sagittal">
      <img src="/images/visual-sagittal.png" alt="visual-sagittal" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 1 — The vocal instrument: power source (lungs/diaphragm), vibrator (larynx/vocal folds), and resonator (the entire tract above).</div>

    <div class="subsection">
      <div class="subsection-title">The larynx close-up</div>
      <div class="subsection-body">
        The larynx is a cartilaginous structure sitting in the throat, housing the vocal folds. The key structures are: the <strong>thyroid cartilage</strong> (the "Adam's apple" — the front wall), the <strong>cricoid cartilage</strong> (the ring below), the <strong>arytenoid cartilages</strong> (small paired structures at the back that open and close the folds), and the <strong>cricothyroid (CT) muscle</strong> which stretches the folds to raise pitch. The <strong>thyroarytenoid (TA) muscle</strong> is the body of the fold itself — its contraction thickens and shortens the fold, lowering pitch and adding mass.
      </div>
    </div>

    <!-- GEMINI_VISUAL id="visual-larynx"
         SUBJECT: Close-up diagram of the larynx, showing vocal fold states
         SHOW TWO PANELS side by side:
           Panel A — "Adducted (singing/speaking)": folds together, airway closed, ready to vibrate
           Panel B — "Abducted (breathing)": folds apart, open airway
         LABELS: Thyroid cartilage, Cricoid cartilage, Arytenoid cartilages,
           Vocal folds (true folds), False folds (ventricular folds), Glottis (the gap),
           Trachea below
         STYLE: Top-down view (as seen through a laryngoscope). Dark background.
           Fold tissue in warm pinkish #c47a5a. Cartilage in blue-grey. Labels in #e8395a lines.
         SIZE: approximately 600px wide × 320px tall (wide, two-panel) -->
    <div class="visual-block" id="visual-larynx">
      <img src="/images/visual-larynx.png" alt="visual-larynx" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 2 — Vocal fold states viewed from above. Left: adducted for phonation. Right: abducted for breathing.</div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-signal-chain">
    <div class="section-kicker">Section 2</div>
    <div class="section-title">How Singing Works: The Signal Chain</div>
    <div class="section-body">
      A sung note is not a simple event — it's the end product of a chain of coordinated actions. Understanding the chain helps identify where a technique operates and why certain combinations are anatomically impossible.
    </div>

    <div class="signal-chain">
      <div class="signal-step">
        <div class="signal-step-num">1 · Power</div>
        <div class="signal-step-name">Breath Pressure</div>
        <div class="signal-step-desc">Diaphragm descends; lungs fill. Controlled exhale builds subglottal pressure below the folds.</div>
      </div>
      <div class="signal-arrow">→</div>
      <div class="signal-step">
        <div class="signal-step-num">2 · Source</div>
        <div class="signal-step-name">Fold Vibration</div>
        <div class="signal-step-desc">Folds adduct (close). Breath pressure forces them apart; surface tension snaps them back. Cycle repeats at pitch frequency.</div>
      </div>
      <div class="signal-arrow">→</div>
      <div class="signal-step">
        <div class="signal-step-num">3 · Raw Tone</div>
        <div class="signal-step-name">Buzzy Source Signal</div>
        <div class="signal-step-desc">The raw vibration is a buzzy, harmonically rich but unmusical sound — like a duck call. This is the "source."</div>
      </div>
      <div class="signal-arrow">→</div>
      <div class="signal-step">
        <div class="signal-step-num">4 · Filter</div>
        <div class="signal-step-name">Tract Resonance</div>
        <div class="signal-step-desc">The vocal tract above amplifies certain harmonics and damps others, depending on its shape. This filtering IS the vowel and the tonal color.</div>
      </div>
      <div class="signal-arrow">→</div>
      <div class="signal-step">
        <div class="signal-step-num">5 · Output</div>
        <div class="signal-step-name">Sung Note</div>
        <div class="signal-step-desc">The shaped, resonated tone exits at the lips (and nose). What we hear.</div>
      </div>
    </div>

    <div class="note-box">
      <strong>Source vs. Filter:</strong> This is the most important concept in voice science. The <em>source</em> (what the folds do) and the <em>filter</em> (what the tract does) are independent variables. A singer can change their tonal color entirely by reshaping the tract without changing the fold configuration — and vice versa. Axis 1 tags (resonance placement) operate on the <em>filter</em>. Axis 2 tags (cord mechanism, register, texture) operate on the <em>source</em>. This is why they can be mixed freely across axes but have exclusion rules within each axis.
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-registers">
    <div class="section-kicker">Section 3</div>
    <div class="section-title">Vocal Registers</div>
    <div class="section-body">
      A "register" in voice science means a range of pitches produced by a consistent vibratory pattern in the folds. As pitch rises, the fold configuration must change — and each major change in how the folds vibrate constitutes a register shift. Most untrained singers feel these shifts as "breaks" or "cracks." Trained singers either smooth them (blend) or exploit them deliberately (break as expression).
    </div>

    <div class="register-map">
      <div class="register-map-title">Register Map — Low to High</div>
      <div class="register-bar"></div>
      <div class="register-labels">
        <span>Fry</span><span>Chest</span><span>Mix / Passaggio</span><span>Head / Falsetto</span><span>Whistle</span>
      </div>
      <div class="register-zones">
        <div class="rz">
          <div class="rz-name">Fry Register</div>
          <div class="rz-tags">fry-scream (substrate use)</div>
        </div>
        <div class="rz">
          <div class="rz-name">Chest Register</div>
          <div class="rz-tags">chest-conversational · belting · grit-rasp · growl-distortion</div>
        </div>
        <div class="rz">
          <div class="rz-name">Passaggio Zone</div>
          <div class="rz-tags">compression · mask-pinched · mask-chest-mix</div>
        </div>
        <div class="rz">
          <div class="rz-name">Head / Falsetto</div>
          <div class="rz-tags">head-mask · full-head-voice · falsetto · covered-voice</div>
        </div>
        <div class="rz">
          <div class="rz-name">Whistle</div>
          <div class="rz-tags">whistle-register</div>
        </div>
      </div>
    </div>

    <!-- GEMINI_VISUAL id="visual-registers"
         SUBJECT: Diagram showing vocal fold configurations across registers
         SHOW FOUR PANELS in a row (or 2x2 grid):
           Panel 1 "Chest / Modal" — thick folds, full contact along length, strong chest resonance arrow
           Panel 2 "Falsetto" — thin fold edges only, slight gap in middle (incomplete closure), light
           Panel 3 "Head Voice" — thin folds, complete closure but CT-dominant, different from falsetto
           Panel 4 "Whistle" — tiny aperture, almost no fold contact, air whistle through pinhole
         Each panel: side-view cross-section of the fold showing tissue mass and contact pattern.
         LABELS per panel: fold thickness, contact pattern, dominant muscle (TA vs CT),
           subglottal pressure level
         STYLE: Dark bg, warm pinkish fold tissue, labels in red/white. Clear contrast between panels.
         SIZE: approximately 640px wide × 280px tall -->
    <div class="visual-block" id="visual-registers">
      <img src="/images/visual-registers.png" alt="visual-registers" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 3 — Vocal fold configurations across registers. Chest voice uses full fold mass; falsetto uses thin edges only; head voice uses thin but fully closing folds; whistle uses a tiny aperture.</div>

    <div class="mechanic-grid">
      <div class="mechanic-card">
        <div class="mechanic-card-title">Chest Voice / Modal</div>
        <div class="mechanic-card-tag">chest-conversational · belting · mask-chest-mix</div>
        <div class="mechanic-card-body">
          <strong>TA (thyroarytenoid) dominant.</strong> Full fold mass vibrates along the entire length. Thick, heavy contact. The low and middle range home for most voices. "Chest" refers to the sympathetic resonance felt in the sternum. Belting is this register driven above its natural ceiling.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Falsetto</div>
        <div class="mechanic-card-tag">falsetto</div>
        <div class="mechanic-card-body">
          <strong>CT (cricothyroid) dominant, TA relaxed.</strong> Only the thin mucosal edges vibrate — the bulk of the fold is passive. Incomplete glottal closure (slight gap) produces the characteristic airy or "hollow" quality. Distinct from head voice, which also uses thin folds but with more complete closure.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Head Voice</div>
        <div class="mechanic-card-tag">head-mask · full-head-voice · compression</div>
        <div class="mechanic-card-body">
          <strong>CT dominant, but complete closure.</strong> Folds are thinned and elongated but still close fully each cycle — unlike falsetto. Produces the ringing, non-airy quality of classical head voice. The "head" feeling is resonance in the cranial bones and upper tract.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Whistle Register</div>
        <div class="mechanic-card-tag">whistle-register</div>
        <div class="mechanic-card-body">
          <strong>Separate mechanism entirely.</strong> Not an extension of head voice. The folds form a tiny aperture and sound is generated by air turbulence through that constriction — closer to literal whistling than vocal fold vibration in the normal sense. Only a handful of singers achieve reliable pitch control here.
        </div>
      </div>
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-resonance">
    <div class="section-kicker">Section 4</div>
    <div class="section-title">Resonance &amp; Placement</div>
    <div class="section-body">
      The raw source signal from the folds passes through the vocal tract, which acts as a variable filter. Depending on the shape of the tract — position of the larynx, tongue height, soft palate position, lip shape — different harmonics are amplified. This is why a baritone and a soprano can sing the same note and sound nothing alike: same source pitch, radically different tract configuration.
    </div>

    <!-- GEMINI_VISUAL id="visual-resonance"
         SUBJECT: Resonance chambers diagram — showing which zones amplify sound for different placements
         SHOW: Sagittal head/neck outline (simpler than Fig 1 — just the silhouette)
         with COLORED ZONES overlaid:
           Zone A "Chest" — chest/sternum area, color #3b5bdb (cool blue)
           Zone B "Throat / Pharynx" — pharynx behind tongue root, color #7c3aed (purple)
           Zone C "Mouth / Oral" — oral cavity, color #ff6b35 (orange)
           Zone D "Mask / Nasal" — nasal cavity and sinuses, color #e8395a (red)
           Zone E "Head" — top of cranium, color #ec4899 (pink)
         Each zone labeled with: zone name + the Building A Rockstar technique tags that activate it
         e.g. Zone D "Mask / Nasal" → mask-singing · mask-narrow · mask-pinched · country-twang · witch-voice
         STYLE: Zones semi-transparent overlays on dark silhouette. Labels outside the silhouette with lines.
         SIZE: approximately 500px wide × 480px tall -->
    <div class="visual-block" id="visual-resonance">
      <img src="/images/visual-resonance.png" alt="visual-resonance" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 4 — Resonance placement zones. Each zone corresponds to a cluster of Axis 1 technique tags. Most singers use multiple zones simultaneously; the dominant zone defines the placement tag.</div>

    <div class="subsection">
      <div class="subsection-title">The soft palate: the resonance router</div>
      <div class="subsection-body">
        The <strong>soft palate</strong> (velum) is the movable rear portion of the roof of the mouth. It controls access to the nasal cavity. <strong>Raised</strong> = nasal passage blocked = oral/pharyngeal resonance only (covered-voice, chest-conversational). <strong>Lowered</strong> = nasal passage open = nasal resonance engaged (mask-singing, mask-narrow, country-twang). This is why covered-voice and any mask tag are anatomically mutually exclusive — the raised soft palate that creates covered warmth physically prevents nasal resonance. You cannot simultaneously block and open the same pathway.
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">Larynx position: the darkness dial</div>
      <div class="subsection-body">
        Larynx position is one of the most powerful tonal controls available to a singer. A <strong>lowered larynx</strong> lengthens the vocal tract, shifting resonance downward and creating warmth, depth, and dark color — this is the defining feature of <span class="tag">covered-voice</span>. A <strong>raised larynx</strong> shortens the tract, brightening and thinning the tone — associated with <span class="tag">belting</span> and <span class="tag">witch-voice</span>. Most singers have a natural resting position somewhere in the middle; conscious larynx control is a trained skill.
      </div>
    </div>

    <!-- GEMINI_VISUAL id="visual-larynx-position"
         SUBJECT: Two-panel diagram showing larynx position effects
         Panel A "Low Larynx (Covered Voice)" — larynx sitting low in the neck, longer tract above,
           arrows showing extended resonance column, warm/dark color indication
         Panel B "High Larynx (Belting / Witch Voice)" — larynx raised, shorter tract,
           constricted pharynx, brighter/sharper indication
         LABELS: larynx position, tract length, pharynx width, resulting tonal character
         STYLE: Side-view neck diagram. Dark bg. Larynx highlighted in accent color.
           Tract length shown with bracket/arrow. Simple and clear.
         SIZE: approximately 580px wide × 300px tall -->
    <div class="visual-block" id="visual-larynx-position">
      <img src="/images/visual-larynx-position.png" alt="visual-larynx-position" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 5 — Larynx position as a tonal control. Low = warm/dark (covered-voice). High = bright/constricted (belting, witch-voice).</div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-passaggio">
    <div class="section-kicker">Section 5</div>
    <div class="section-title">The Passaggio: Negotiating the Break</div>
    <div class="section-body">
      The <strong>passaggio</strong> (Italian: "passage") is the zone of pitches where the chest-dominant fold configuration must transition toward a CT-dominant configuration. For most male voices this sits around E4–G4; for most female voices around D5–F5. The transition is mechanical — at a certain pitch the fully massed fold configuration simply cannot sustain efficient vibration. Something has to give. <em>How</em> a singer handles that transition is one of the most defining aspects of their vocal identity — and it's exactly what most of the Axis 2 tags describe.
    </div>

    <!-- GEMINI_VISUAL id="visual-passaggio"
         SUBJECT: Diagram of the four passaggio strategies
         SHOW FOUR PANELS arranged as a 2x2 grid or horizontal flow, labeled:
           Strategy 1 "Pinching" (mask-pinched + compression):
             — folds still massed, larynx raising, nasal constriction engaged to prop up the note
             — result: nasal pressed quality, limited ceiling, sounds effortful
           Strategy 2 "Belting":
             — chest register driven openly above break, larynx raised, no reorganization
             — result: raw, powerful, ceiling comes fast
           Strategy 3 "Compression":
             — CT beginning to dominate but TA still engaged, pressed/glottal quality
             — result: "held back" sound, can be controlled, Vedder/Cornell territory
           Strategy 4 "Head-Mask":
             — full reorganization: CT dominant, TA releases, folds thin, note floats free
             — result: ringing, released, effortless-feeling above the break
         For each panel show a simplified fold cross-section + a brief label of what the singer feels/sounds like
         STYLE: 2x2 grid. Each panel has light border. Dark bg. Folds in warm pink.
           Muscle labels CT/TA. Arrow showing direction of laryngeal movement.
         SIZE: approximately 640px wide × 400px tall -->
    <div class="visual-block" id="visual-passaggio">
      <img src="/images/visual-passaggio.png" alt="visual-passaggio" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 6 — The four passaggio strategies, ordered by degree of laryngeal reorganization. Pinching and belting avoid reorganization; compression manages the transition; head-mask completes it.</div>

    <div class="note-box red">
      <strong>Why the break matters for karaoke:</strong> Most male karaoke singers operate almost entirely in chest register and hit the passaggio around E4–G4. Songs in this zone (or higher) demand either a passaggio strategy or a key change. Recognizing which strategy an artist uses on their recording tells you what you'd need to replicate the sound — and whether it's in your wheelhouse. A baritone with good compression (Cornell, Vedder) can do a lot that a chest-only singer can't. A singer with reliable head-mask (Steven Tyler, Freddie Mercury) can extend that further still.
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-distortion">
    <div class="section-kicker">Section 6</div>
    <div class="section-title">Distortion Mechanisms</div>
    <div class="section-body">
      The term "distortion" in voice science refers to the involvement of structures <em>other than</em> the true vocal folds in phonation — specifically, structures above the folds that add turbulence, noise, or secondary vibration to the source signal. When done correctly these are supraglottic (above the folds): the folds themselves remain healthy and undamaged. The three main mechanisms in our taxonomy map to distinct anatomical locations.
    </div>

    <!-- GEMINI_VISUAL id="visual-distortion"
         SUBJECT: Three-panel diagram of distortion mechanisms by anatomical location
         SHOW THREE PANELS:
           Panel 1 "Fry / Creak (Closure-Phase Turbulence)":
             — the folds themselves vibrating at very slow, irregular rate with long closed phase
             — loose, low-tension fold contact, slow cycle
             — label: "Source-level — folds doing it directly"
             — tag: fry-scream (Brian Johnson mode)
           Panel 2 "Grit / Rasp (Supraglottic Turbulence)":
             — true folds vibrating normally below, PLUS aryepiglottic region narrowing above
             — turbulence generated in the space just above the folds
             — label: "High above folds — bright, won't damage true folds"
             — tag: grit-rasp
           Panel 3 "Growl / Distortion (False Fold / Arytenoid Engagement)":
             — true folds vibrating below, PLUS false (ventricular) folds partially adducted
             — or arytenoids vibrating against epiglottis
             — darker, lower-frequency secondary vibration underneath the fundamental
             — label: "False folds engaged — darker rumble beneath the note"
             — tag: growl-distortion
         STYLE: Side cross-section of larynx. Highlight active structure in each panel in accent color.
           True folds = warm pink. False folds = orange. Aryepiglottic area = red.
         SIZE: approximately 640px wide × 360px tall (three-panel horizontal) -->
    <div class="visual-block" id="visual-distortion">
      <img src="/images/visual-distortion.png" alt="visual-distortion" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 7 — Three distortion mechanisms by anatomical location. All are supraglottic when healthy — the true folds are not being damaged. The location determines the sound: source = fry, high above = grit, false folds = growl.</div>

    <div class="mechanic-grid">
      <div class="mechanic-card">
        <div class="mechanic-card-title">Fry / Creak Register</div>
        <div class="mechanic-card-tag">fry-scream</div>
        <div class="mechanic-card-body">
          Folds vibrate at very low frequency with a long closed phase — the "pulse register." Source-level phenomenon. Brian Johnson uses this as his <em>primary phonation mechanism</em> for melody, not as a texture applied over it. Most rock singers use fry as a substrate that other mechanisms run above.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Grit / Rasp</div>
        <div class="mechanic-card-tag">grit-rasp</div>
        <div class="mechanic-card-body">
          Turbulence generated in the aryepiglottic space — high in the throat, above the true folds. Bright, high-frequency texture. Never drops into growl territory because the generating structure is too far up the tract. Safe when calibrated; the risk zone is pressing the true folds hard and calling it grit.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">Growl / Distortion</div>
        <div class="mechanic-card-tag">growl-distortion</div>
        <div class="mechanic-card-body">
          False (ventricular) folds adduct partially, creating a secondary vibrating surface below the primary tone. Alternatively: arytenoid cartilages vibrate against the epiglottis. The result is a lower, darker rumble sitting <em>under</em> the fundamental — not texture on top of it, but a second voice beneath.
        </div>
      </div>
      <div class="mechanic-card">
        <div class="mechanic-card-title">CVT Mapping</div>
        <div class="mechanic-card-tag">reference</div>
        <div class="mechanic-card-body">
          Complete Vocal Technique distinguishes: <strong>Distortion</strong> (false/ventricular folds) vs. <strong>Growl</strong> (arytenoids against epiglottis) vs. <strong>Grunt</strong> (whole supraglottic structure, large amplitude). All three are anatomically distinct despite sounding broadly similar. Building A Rockstar's growl-distortion tag covers the first two; grunt is separate.
        </div>
      </div>
    </div>
  </section>

  <hr class="divider">

  <!-- ═══════════════════════════════════════════════════════════════════ -->
  <section class="section" id="ap-breath">
    <div class="section-kicker">Section 7</div>
    <div class="section-title">Breath Support &amp; Subglottal Pressure</div>
    <div class="section-body">
      "Support" is the most overused and least explained term in vocal pedagogy. What it actually means: controlled management of subglottal air pressure — the air pressure below the folds — to maintain efficient fold vibration at the desired pitch and volume without allowing excess pressure to blow the folds apart (breathy) or forcing them together so hard they strain (pressed/glottal).
    </div>

    <!-- GEMINI_VISUAL id="visual-breath"
         SUBJECT: Breath support diagram — diaphragm, intercostals, abdominals
         SHOW: Front-view torso diagram showing:
           — Diaphragm (dome shape below lungs, descends on inhale)
           — External intercostal muscles (rib cage expansion, inhalation)
           — Internal intercostals (rib depression, exhalation control)
           — Abdominal muscles (anterior abdominal wall, exhalation support)
           — Lung volume indicator (full vs. depleted)
           — Arrow showing direction of airflow upward to the larynx
         TWO STATES: Inhalation (diaphragm descends, ribs expand) and Controlled Exhalation for singing
           (diaphragm held, abs providing controlled pressure rather than collapsing suddenly)
         LABELS: Diaphragm, External intercostals, Internal intercostals,
           Rectus abdominis / Transverse abdominis, Trachea, direction arrows
         STYLE: Front-view anatomical. Dark bg. Muscle groups in different colors.
           Warm anatomical palette. Clear labels.
         SIZE: approximately 500px wide × 500px tall -->
    <div class="visual-block" id="visual-breath">
      <img src="/images/visual-breath.png" alt="visual-breath" style="width:100%;display:block;border-radius:0;">
    </div>
    <div class="visual-caption">Fig 8 — Breath support musculature. The diaphragm descends to fill the lungs; the abdominal and intercostal muscles manage the controlled release of that air during singing.</div>

    <div class="subsection">
      <div class="subsection-title">Appoggio: the classical approach</div>
      <div class="subsection-body">
        The classical technique called <strong>appoggio</strong> (Italian: "to lean") involves maintaining the expanded rib cage position of inhalation while singing — the intercostals hold the ribs open, counteracting the natural tendency to collapse as air depletes. This creates a "managed release" of air pressure: the diaphragm descends to fill, then the abdominals and intercostals coordinate to provide a steady, controlled outflow rather than a sudden dump of pressure. The result is consistent subglottal pressure across a long phrase — the physiological basis of what teachers describe as "support."
      </div>
    </div>

    <div class="subsection">
      <div class="subsection-title">Breath as tone color: breath-smoke</div>
      <div class="subsection-body">
        The <span class="tag">breath-smoke</span> technique is a deliberate choice to <em>not</em> support fully — to allow some excess air to pass through the folds alongside the tone, creating breathy, intimate color. Sade, Seal, John Mayer. Physiologically: incomplete glottal closure allows air to leak through during the open phase of each vibration cycle. This is the same thing that causes untrained singers to "run out of air" — but when applied intentionally and controlled dynamically, it's an expressive tool.
      </div>
    </div>
  </section>

  <hr class="divider">

  <div class="note-box purple">
    <strong>Disclaimer:</strong> The anatomical and physiological descriptions on this page represent plausible functional hypotheses derived from auditory analysis and published voice science. They are not clinical findings. For rigorous scientific grounding: <strong>Complete Vocal Technique</strong> (Cathrine Sadolin, CVI) and <strong>The Estill Voice Model</strong> (Jo Estill / Kimberly Steinhauer) are the most rigorous style-independent frameworks available. For academic voice science: <em>The Science of the Singing Voice</em> (Johan Sundberg) is the standard reference text.
  </div>

  <div style="text-align:center; margin-top: 32px;">
    <a href="#" onclick="showVgPage('technique',document.getElementById('vgnav-technique'));return false;" style="display:inline-flex;align-items:center;gap:6px;padding:10px 20px;border-radius:20px;border:1px solid rgba(124,58,237,0.4);background:rgba(124,58,237,0.08);color:#a78bfa;font-size:13px;font-weight:600;text-decoration:none;">
      📖 Voice Technique Guide — 26 Techniques with Exemplars →
    </a>
  </div>

  <div style="font-size:10px;color:#3a3840;text-align:right;margin-top:24px;">
    Building A Rockstar Voice A&P · v1.0 · April 2026 · 8 diagrams (Gemini visual placeholders)
  </div>

</div>
          </div><!-- end vg-toc-content -->
        </div><!-- end vg-toc-layout -->
      </div>

      <!-- PAGE: TECHNIQUE -->
      <div id="vgp-technique" class="vgp-guide vg-page" style="display:none;">
        <div class="vg-toc-layout">
          <div class="vg-toc-pane" id="vgtoc-technique">
            <div class="vg-toc-inner">
              <span class="vg-toc-section-label">Technique</span>
              <a class="vg-toc-link" onclick="vgTocGo('tech-register')">Register</a>
              <a class="vg-toc-link" onclick="vgTocGo('tech-resonance')">Resonance</a>
              <a class="vg-toc-link" onclick="vgTocGo('tech-texture')">Texture</a>
              <a class="vg-toc-link" onclick="vgTocGo('tech-phrasing')">Phrasing</a>
            </div>
          </div>
          <div class="vg-toc-content">
        <div class="content">
  <p class="page-sub">36 vocal techniques across four sections. All are learnable — some take longer than others.</p>

  <a href="#" onclick="showVgPage('anatomy',document.getElementById('vgnav-anatomy'));return false;" class="ap-link">🫁 Voice Anatomy &amp; Physiology →</a>

  <div class="note">
    <strong>This is a perceptual cataloging system</strong>, not a clinical one. The goal is to describe what you hear and what a singer is doing from the outside in — ear to theory, not laryngoscopy to sound. Where we describe what is "happening in the larynx," we mean: here is a rational account of what might be occurring mechanically to produce this sound. Analytical lenses, not clinical diagnoses.
    <br><br>
    <strong>Four independent axes — one from each section:</strong>
    <br><br>
    <strong>Register</strong> — what phonation mode is the larynx in? Modal · Head Voice · Witch Voice · Falsetto · Belt · Whistle. Every singer is always in one of these.<br><br>
    <strong>Resonance</strong> — where is the sound predominantly amplifying? Two families: a Modal family (Nasal Harsh · Nasal Neutral · Nasal Sweet · Nasal/Chested · Open Chest) and a Head Voice family (Head · Head/Mask · Mask Sweet · Mask Narrow). Every singer has a primary resonance placement.<br><br>
    <strong>Texture</strong> — what modifiers are applied to the base signal? Rasp · Growl · Fry · Compression · Vibrato · Straight Tone · Breath/Smoke · Covered. Optional — not every singer uses texture modifiers.<br><br>
    <strong>Phrasing</strong> — what stylistic motor patterns define the delivery? Speak-Sing · Runs &amp; Riffs · Portamento · Grunt · Microtonal · Rapid Fire · Yodel/Break. Optional — not every singer has a defining phrasing technique.
    <br><br>
    Tags are not mutually exclusive across sections — a complete voice profile combines one Register tag, one Resonance tag, and any number of Texture and Phrasing tags. Within each section, exclusion rules apply — you cannot simultaneously be in Modal and Head Voice register, or simultaneously have Chest and Head resonance as primary placements.
  </div>

  <div class="note">
    <strong>A note on "mixed voice."</strong> If you've studied singing elsewhere, you may wonder where "mix" went. We leave it out on purpose. "Mix" is really three different ideas sharing one word: (1) the blend of fold-muscle activity every singer uses to cross their range — true of nearly everyone, so it barely describes anything; (2) a <em>tone</em> that marries the body of chest to the ring of head — the genuinely useful sense; and (3) the cluster of notes around your passaggio — a pitch range, not a technique.
    <br><br>
    Because this system keeps <strong>Register</strong> (what the larynx is doing) and <strong>Resonance</strong> (where the sound rings) on separate axes, the useful sense of "mix" emerges as a <em>combination</em> instead of needing its own box. A Head register carried over Open-Chest resonance is a "chesty mix"; Head register with Mask resonance is a "heady mix." Frameworks that bundle register and resonance into one dimension need a dedicated "mix" mode to name that blend — we don't, because we've already separated the two things being blended. So mix isn't missing here. It lives wherever two axes meet.
  </div>

  <div class="note purple">
    <strong>⚠️ Reference frameworks:</strong> For rigorous anatomical and physiological grounding beyond what this guide provides, consult <strong>Complete Vocal Technique</strong> (Cathrine Sadolin, CVI) — four vocal modes with peer-reviewed laryngoscopic data — and <strong>The Estill Voice Model</strong> (Jo Estill / Kimberly Steinhauer) — 13 independently controllable anatomical structures mapped to six Voice Qualities. Both are more mechanistically precise than this guide; this guide trades precision for breadth and usability.
  </div>

  <div class="legend">
    <div class="legend-item"><div class="legend-dot" style="background:var(--reg)"></div>Register</div>
    <div class="legend-item"><div class="legend-dot" style="background:var(--res)"></div>Resonance</div>
    <div class="legend-item"><div class="legend-dot" style="background:var(--tex)"></div>Texture</div>
    <div class="legend-item"><div class="legend-dot" style="background:var(--phr)"></div>Phrasing</div>
  </div>

  <!-- ═══ REGISTER ══════════════════════════════════════════════════════════ -->
  <div class="vg-cat" id="tech-register" style="--vc:var(--reg)">
    <div class="vg-cat-label">Register · Laryngeal Setup</div>

    <div class="vg-card">
      <div class="vg-card-name">Modal / Speech Voice</div><div class="vg-card-id">REG-modal</div>
      <div class="vg-card-body">The laryngeal setup you use for speech — no fundamental reorganization of the fold configuration. The thyroarytenoid (TA) muscle dominates; folds vibrate with full or near-full mass. This is the home register for most of the range in most singers. It is the implicit default — most technique tags in the Resonance and Texture sections describe what is happening <em>within</em> modal register. Modal voice can never produce the ringing clarity of a true laryngeal Head Voice reorganization, regardless of how forward or "head"-feeling the resonance placement is. The ceiling is real and fixed by anatomy.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Full / Head Voice</div><div class="vg-card-id">REG-head</div>
      <div class="vg-card-body">A genuinely reorganized laryngeal setup in which the cricothyroid (CT) muscle dominates — folds thin, elongate, and close fully each cycle. The TA releases. This is a fundamentally different phonation mode from modal register and produces a qualitatively different ring that modal placement, however "head"-sounding, cannot replicate. The ringing, sourceless quality of full head voice travels through pharyngeal and upper tract resonance pathways distinct from the nasal port — which is why covered warmth (nasal port closed) and head voice ring can coexist simultaneously. Classical operatic tenors demonstrate both at once.
      <br><br>
      Within this laryngeal setup, resonance can live in several places — see the Resonance section for Head, Head/Mask, and the interaction with Covered. Can co-occur with Breath/Smoke, Vibrato, or distortion textures at the cord level. <strong>Mutually exclusive with Modal, Belt, and Witch Voice.</strong></div>
      <div class="vg-card-foot">CT = cricothyroid muscle (pitch-raiser, fold-thinner). TA = thyroarytenoid (fold body, mass contributor). Head Voice = CT dominant, full closure. Falsetto = CT dominant, incomplete closure.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Witch Voice</div><div class="vg-card-id">REG-witch</div>
      <div class="vg-card-body">A pharyngeal constriction-based strategy for accessing high notes — raised larynx with deep aryepiglottic narrowing. Incompatible with the neutral modal speech setup, and distinct from Head Voice (CT-reorganized, released). The voice cuts and pierces rather than rings or booms. A legitimate full-voice high-note pathway widely used in rock, requiring its own laryngeal configuration.
      <br><br>
      <strong>Robert Plant (Led Zeppelin)</strong> (Immigrant Song — the definitive example; rings and balances rather than merely abrades) · <strong>Chris Cornell (Soundgarden), Layne Staley (Alice in Chains)</strong> (upper register) · <strong>Van Morrison</strong> (primary tonal identity throughout the range) · <strong>early Geddy Lee (Rush)</strong> (upper register) · <strong>Axl Rose (Guns N' Roses)</strong> (high notes — rawer and less balanced than Plant).
      <br><br>
      Note: the nasal constriction variety — singers like Billy Corgan where the constriction is forward rather than pharyngeal — is now categorized as <strong>TEX-modal-pinch</strong> in the Texture section, not as Witch Voice. Witch Voice in this taxonomy refers specifically to pharyngeal constriction.</div>
      <div class="vg-card-foot">Pedagogy term: pharyngeal resonance / twang. Related to CVT's Edge mode. Not the same as Country Twang. The defining perceptual quality: the voice cuts rather than rings.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Falsetto</div><div class="vg-card-id">REG-falsetto</div>
      <div class="vg-card-body"><strong>Prince, Justin Timberlake, Bee Gees, Lenny Kravitz</strong> (It Ain't Over Till It's Over) — CT dominant like Head Voice, but with incomplete glottal closure — a slight gap through which air leaks, producing the characteristic airy or hollow quality that distinguishes falsetto from full head voice (which closes fully each cycle). The airiness is a byproduct of the incomplete closure, not a deliberate Breath/Smoke application. Resonance placement operates independently: falsetto + Mask Sweet (JT, Bee Gees — bright and forward); falsetto + Head/Mask (Prince in some passages); falsetto + Breath/Smoke (Jeff Buckley — haunting, fragile). <strong>Excludes Chest and Mask/Chest resonance placements.</strong></div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Belt</div><div class="vg-card-id">REG-belt</div>
      <div class="vg-card-body">Operating at yell-level output rather than speech or loud-speech level. The entire system — diaphragm, subglottal pressure, laryngeal position — shifts into the configuration you use when you actually yell. This is a distinct laryngeal setup, not merely "singing louder." The passaggio still exists and must be navigated even in belt mode; most contemporary belters use some CT engagement above the break while maintaining chest weight dominance (mixed belt). Pure speech-level belt with no CT contribution hits its ceiling fast.
      <br><br>
      Belt is a Texture modifier applied on top of resonance: you can belt from Chest, from Mask/Chest, or in principle from any resonance placement — though the chest weight requirement means it most naturally pairs with Mask/Chest and Chest resonance. The "slightly uncontrolled" quality is diagnostic — belt has an audible commitment and rawness that distinguishes it from simply powerful singing.
      <br><br>
      <strong>Chest belt:</strong> Alanis Morissette, Melissa Etheridge — hard ceiling, raw top. <strong>Mask/Chest belt:</strong> Whitney Houston, Bon Jovi, Celine Dion — more bloom, more range. <strong>Brandi Carlile</strong> (The Story) — rare female example of belt plus Rasp simultaneously.</div>
      <div class="vg-card-foot">Perceptual test: if removing the effort and rawness leaves a still-powerful but less committed sound, it was belting. Belt without chest weight is a contradiction — the chest weight IS the belt.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Whistle Register</div><div class="vg-card-id">REG-whistle</div>
      <div class="vg-card-body"><strong>Mariah Carey, Minnie Riperton</strong> — <em>Extreme.</em> A completely separate phonation mechanism, not an extension of Head Voice or Falsetto. The folds form a tiny aperture and sound is produced by air turbulence through that constriction — closer to literally whistling than normal fold vibration. Only a handful of singers achieve reliable pitch control here. Resonance placement tags are largely irrelevant at whistle register frequencies — the pitch range overwhelms normal chamber distinctions. The most extreme register in the taxonomy.</div>
    </div>

  </div>

  <!-- ═══ RESONANCE ═════════════════════════════════════════════════════════ -->
  <div class="vg-cat" id="tech-resonance" style="--vc:var(--res)">
    <div class="vg-cat-label">Resonance · Placement</div>

    <div class="note" style="margin-bottom:12px;">
      Resonance tags describe where the sound predominantly amplifies. Two distinct families — <strong>Modal</strong> and <strong>Head Voice</strong> — reflect fundamentally different resonance strategies depending on which register the singer is in. Modal resonance tags describe the nasal character of a voice in speech-level register; Head Voice tags describe intentional resonance placement in CT-reorganized register. The Covered texture modifier can shift the tonal character at any position without changing the primary placement.
      <br><br>
      <strong>Key principle:</strong> forward or nasal resonance does not mean Head Voice register. Modal voice can sound very nasal or very forward — that is a resonance character, not a register. The Head Voice family tags apply only when the larynx has genuinely reorganized above the passaggio.
    </div>

    <div class="vg-cat-label" style="margin-top:4px;margin-bottom:8px;font-size:8px;letter-spacing:1.5px;">── MODAL RESONANCE FAMILY ──</div>

    <div class="vg-card">
      <div class="vg-card-name">Nasal (Harsh)</div><div class="vg-card-id">RES-nasal-harsh</div>
      <div class="vg-card-body">High nasal component, unpleasant or intrusive. A listener would spontaneously describe the voice as nasal. The <em>"wow, that's nasal"</em> test passes. The nasality is not balanced by chest weight or sweetness — it is simply there as the dominant tonal character.
      <br><br>
      <strong>Michael Stipe (R.E.M.), Ed Kowalczyk (Live), Wes Scantlin (Puddle of Mudd), Whitfield Crane (Ugly Kid Joe), Brandon Boyd (Incubus), Doug Robb (Hoobastank)</strong>
      <br><br>
      Note: perceived sweetness in delivery, dynamics, or material does not override a genuinely nasal tone. Tag the resonance, not the emotional character of the performance.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Nasal (Neutral)</div><div class="vg-card-id">RES-nasal-neutral</div>
      <div class="vg-card-body">Mild nasal component, neither pleasant nor unpleasant — unremarkable in either direction. A listener would not spontaneously comment on the nasality. Probably the most populated category in practice for modal singers: the majority of "normal" tenorish voices with no particular resonance strategy land here.
      <br><br>
      <strong>Glen Phillips (Toad the Wet Sprocket), Emerson Hart (Tonic), Robin Wilson (Gin Blossoms), early Rob Thomas (Matchbox Twenty)</strong></div>
      <div class="vg-card-foot">The "unremarkable tenor" zone — competent, pleasant, uneventful. The singing voice is simply the speaking voice, with a mild nasal component that is neither distinctive nor problematic.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Nasal (Sweet)</div><div class="vg-card-id">RES-nasal-sweet</div>
      <div class="vg-card-body">Nasal component is present but pleasant — bright, charming, sometimes boyish. A listener might describe it as bright or sweet rather than "nasal." The forward placement reads positively rather than as an intrusion. No chest weight needed.
      <br><br>
      <strong>Rivers Cuomo (Weezer), early Justin Timberlake (modal sections), Musiq Soulchild</strong> (sweet component — the chest weight is a separate modifier; see Nasal/Chested).</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Nasal / Chested</div><div class="vg-card-id">RES-nasal-chested</div>
      <div class="vg-card-body">Significant nasal component <em>plus</em> significant chest weight operating simultaneously. The chest weight balances or grounds the nasality so it reads as powerful, warm, or present rather than purely harsh or thin. This is a modifier — the nasal flavor underneath can be harsh or sweet depending on the singer; what defines this tag is the chest weight component.
      <br><br>
      <strong>Eddie Vedder (Pearl Jam), Layne Staley (Alice in Chains)</strong> (harsh+chested — the chest weight grounds what would otherwise be a harsher nasal quality) · <strong>Musiq Soulchild</strong> (sweet+chested — warm, full, pleasant) · <strong>Nat King Cole</strong> (sweet/neutral+chested — characteristic warmth comes from this combination)
      <br><br>
      This was previously labeled Mask/Chest — the concept is identical, the name now correctly reflects that these singers are not doing intentional mask placement but rather accessing modal nasal resonance balanced by chest weight.</div>
      <div class="vg-card-foot">Why baritones sound different: the chest weight contribution thickens and warms a nasal tone that in a lighter voice would read as simply nasal. The resonance is the same; the perceived character is different because of the mass underneath it.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Open Chest</div><div class="vg-card-id">RES-open-chest</div>
      <div class="vg-card-body">Chest and oral cavity dominant — minimal to no nasal component. The sound travels from the chest up through the pharynx and exits through the mouth without nasal diversion or mask engagement. Direct, dry, unmanipulated. The modal equivalent of a bell tone — what modal voice sounds like when it's simply working cleanly without any particular resonance strategy.
      <br><br>
      <strong>Bruce Springsteen (rock delivery), John Mellencamp, Joan Jett, Bob Seger (clean moments), early Tom Waits</strong>
      <br><br>
      Note: country singers are rarely if ever Open Chest — country twang implies nasal resonance. Springsteen is the clearest exemplar: that direct, chest-forward quality with no nasal note whatsoever.</div>
      <div class="vg-card-foot">Distinct from Covered — Open Chest has a neutral or slightly raised larynx with no deliberate darkening strategy. Covered adds a low-larynx, soft-palate-raised configuration on top. Both involve minimal nasal resonance, but for different mechanical reasons.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Twang</div><div class="vg-card-id">RES-twang</div>
      <div class="vg-card-body">Aryepiglottic sphincter narrowing — the Estill "twang figure." A bright, cutting, projecting "ping" that carries over loud bands without effort. The epilaryngeal tube narrows, amplifying the singer's formant around 2–3 kHz — the same frequency region that makes a trumpet cut through an orchestra. High-efficiency resonance: loud without pressed folds.
      <br><br>
      <strong>Dolly Parton, Reba McEntire, Hank Williams, Loretta Lynn</strong> (country oral twang) · <strong>Steven Tyler (Aerosmith, upper range), Geddy Lee (Rush)</strong> (rock applications)
      <br><br>
      Country twang = oral twang with the nasal port open, so nasal resonance and twang co-exist. This is why country singers are almost never tagged Open Chest — the nasal port stays open in the twang configuration.</div>
      <div class="vg-card-foot">Key diagnostic: pinch your nose while singing. If the sound barely changes, it's twang, not nasality. Nasality routes air through the nasal passage; twang is a throat shape that happens to sound bright. Distinguished from RES-nasal-harsh: harsh nasal has a spread, unfocused quality; twang is a tight, forward ping. Distinguished from RES-open-chest: Open Chest is neutral modal resonance with no epilaryngeal narrowing.</div>
    </div>

    <div class="vg-cat-label" style="margin-top:16px;margin-bottom:8px;font-size:8px;letter-spacing:1.5px;">── HEAD VOICE RESONANCE FAMILY ──</div>

    <div class="note" style="margin-bottom:12px;">
      Head Voice resonance tags describe intentional placement strategies available in CT-reorganized register. These are not accidental tonal characters — they represent trained or semi-trained resonance decisions made possible by the laryngeal reorganization of head voice. Pairing rules: these tags should accompany REG-head or REG-falsetto, not REG-modal.
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Head</div><div class="vg-card-id">RES-head</div>
      <div class="vg-card-body"><strong>Steve Perry (Journey), Amy Lee (Evanescence)</strong> — Pure upper resonance with no perceptible placement engagement in mask, chest, or throat. The tone feels sourceless. Requires Head Voice register. Rare in pop — most apparent candidates are actually Head/Mask on close listen. Steve Perry combines pure head resonance with significant Breath/Smoke — the breath is a cord-level color applied on top of the resonance architecture. <strong>Mutually exclusive with all Nasal/Modal tags, Open Chest, and Mask Sweet/Narrow.</strong></div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Head / Mask</div><div class="vg-card-id">RES-head-mask</div>
      <div class="vg-card-body"><strong>Freddie Mercury (Queen), Steven Tyler (Aerosmith), JC Chasez (NSYNC), Matt Bellamy (Muse)</strong> (near pure-head end — mask component subtle) — Head Voice register as the primary power mechanism with mask resonance coloring. Notes feel released and floating rather than squeezed — the larynx has genuinely reorganized into Head Voice. The CT is dominant, the TA has released. The clean alternative to constriction strategies above the passaggio. Freddie Mercury is Head/Mask rather than pure Head — his tone is locatable and forward, not sourceless.</div>
      <div class="vg-card-foot">Released quality is the key diagnostic — distinguishes from TEX-modal-pinch (squeezed and effortful) and Belt (committed and raw). If the high note feels like it floated there rather than was pushed, it's Head/Mask.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Mask (Sweet)</div><div class="vg-card-id">RES-mask-sweet</div>
      <div class="vg-card-body"><strong>Michael Jackson, Bruno Mars, Ne-Yo, The Weeknd, Chris Carrabba (Dashboard Confessional)</strong> · <strong>Smokey Robinson, Ron Isley, Curtis Mayfield, Philip Bailey</strong> — Forward, bright, pleasant mask resonance in head voice or upper range. Clear upper harmonics, present without triggering a "that's nasal" response. The sweetness is in the resonance placement, not just the material or delivery.
      <br><br>
      Note: Rivers Cuomo, Justin Timberlake (modal moments), and similar nasal-sweet singers belong in <strong>RES-nasal-sweet</strong>, not here — the distinction is whether the singer is in a genuine head voice reorganization or accessing pleasant nasal resonance modally.</div>
      <div class="vg-card-foot">Distinguishing Mask Sweet from Nasal Sweet: Mask Sweet is a head-voice resonance strategy producing a specific bright ring. Nasal Sweet is pleasant modal nasality. The perceptual line can be subtle — if in doubt, check the register tag.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Mask (Narrow)</div><div class="vg-card-id">RES-mask-narrow</div>
      <div class="vg-card-body"><strong>Ron Isley (peak moments)</strong> — Mask Sweet territory but deliberately focused or constricted into a narrower, more pointed beam. A stylistic choice, not a coping mechanism — the singer is intentionally concentrating the forward placement into a tight, precise focus rather than letting it bloom. The result is a distinctive concentrated sweetness, almost laser-like in its precision.
      <br><br>
      Distinct from TEX-modal-pinch (which is compensatory narrowing due to inability to access head voice) — Mask Narrow is a deliberate resonance aesthetic applied on top of genuine head voice or well-supported upper modal technique.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Covered Voice</div><div class="vg-card-id">TEX-covered</div>
      <div class="vg-card-body"><em>Note: Covered Voice appears in both Resonance and Texture sections. It is a resonance-modifying configuration — listed here for context, tagged as TEX-covered.</em>
      <br><br>
      <strong>Hozier, Luther Vandross, Donny Hathaway, Frank Sinatra, Michael McDonald, Dr. Dre, 2Pac, Bruce Springsteen</strong> (covered foundation under significant Rasp) — Low larynx <em>plus</em> raised soft palate. The soft palate closes the nasal port, routing all resonance into the oral/pharyngeal cavity. Dark, warm, authoritative — and notably without cut or brightness. This is what distinguishes Covered from TEX-cry (see below): Covered specifically closes the nasal port and removes the forward brightness. If the voice is deep and warm but still has cut, it is TEX-cry, not Covered.
      <br><br>
      <strong>Covered voice and head voice ring are compatible.</strong> The ring of Head Voice travels through pharyngeal pathways that the raised soft palate does not block. <strong>Covered voice and nasal resonance are inversely proportional</strong> — fully committed covered voice progressively eliminates nasal resonance. A correctly tagged Covered voice should not simultaneously carry any nasal resonance tags.</div>
      <div class="vg-card-foot">Classical term: voce coperta. Mechanism: low larynx lengthens the vocal tract; raised soft palate closes the velopharyngeal port. A tract configuration, not a geographic destination — which is why it functions as a modifier across the spectrum rather than occupying a fixed position on it.</div>
    </div>

  </div>

  <!-- ═══ TEXTURE ════════════════════════════════════════════════════════════ -->
  <div class="vg-cat" id="tech-texture" style="--vc:var(--tex)">
    <div class="vg-cat-label">Texture · Modifier</div>

    <div class="note" style="margin-bottom:12px;">
      Texture tags are the equivalent of effects pedals for the voice — modifiers applied on top of whatever Register and Resonance setup is already in place. They can in principle combine with any Register/Resonance combination, though some pairings are physically constrained (e.g., Covered tends to reduce nasal resonance, so it modifies but also limits certain Mask placements).
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Rasp</div><div class="vg-card-id">TEX-rasp</div>
      <div class="vg-card-body"><strong>Rod Stewart, Bryan Adams, Sting</strong> (lighter register) · <strong>Bruce Springsteen, Josh Todd (Buckcherry), Chris Stapleton, Gregg Allman</strong> — Surface roughness woven into tone. A mild, consistent overdrive where subglottic pressure and fold closure are slightly mismatched — just enough to produce texture, not enough to cross into crunch. Sandpaper quality, integrated rather than forced. The goldilocks zone just barely exceeded.
      <br><br>
      Think of Rasp as the first step out of clean phonation on the overdrive side — barely past the threshold, which is exactly why it can be sustained for full sets. Grit is the same mechanism at higher pressure. Growl operates deeper and darker. Fry is the opposite direction entirely (under-driven, not over).</div>
      <div class="vg-card-foot">Anatomical location: fold margin / aryepiglottic space (high supraglottic). Stays bright and forward because of where it lives. Contrast: Grit = same location, pushed harder. Growl = false folds, darker, deeper. Fry = under-driven, pressure deficit, inherently quiet.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Grit</div><div class="vg-card-id">TEX-grit</div>
      <div class="vg-card-body"><strong>Brian Johnson (AC/DC), Janis Joplin, Steven Tyler (Aerosmith — peaks), Bon Scott, Tom Keifer (Cinderella)</strong> — Compression past the clean phonation threshold. The same mechanism as Rasp but driven harder: subglottic pressure overdrives fold closure, pushing beyond what the folds can cleanly resist, producing a bright, high-in-the-throat crunch. Still superficial and forward — aryepiglottic / fold-surface level — so it stays cutting and bright, not dark or rolling. The distinction from Rasp is one of intensity and threshold, not location.
      <br><br>
      Brian Johnson is the canonical example: his distortion is forward and bright, not deep or rolling. That confirms this is fold-surface / aryepiglottic — not false fold growl. The goldilocks zone decisively exceeded.</div>
      <div class="vg-card-foot">Goldilocks framework: clean zone → Rasp (barely over) → Grit (clearly over, still superficial) → Growl (fully over, deep structures engaged). Grit and Growl are both overdrive; Grit stays high and bright, Growl goes deep and dark. Fry is the under-driven opposite direction.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Growl</div><div class="vg-card-id">TEX-growl</div>
      <div class="vg-card-body"><strong>Eddie Vedder (Pearl Jam), Chris Cornell (Soundgarden), Kurt Cobain (Nirvana), Layne Staley (Alice in Chains), Dave Grohl (Foo Fighters), Joe Cocker, Sully Erna (Godsmack)</strong> · <strong>Bob Seger</strong> (rolling growl — tumbling, gargling quality distinct from the pressed static distortion of Vedder or Staley) — Deep, dark rumble beneath the fundamental. False (ventricular) folds adduct partially creating a secondary vibrating surface, or arytenoid cartilages vibrate against the epiglottis. A second voice underneath the primary tone, not texture on top of it. Feels like it comes from below the note rather than in front of it.
      <br><br>
      The darkness and depth are the diagnostic. Grit and Rasp stay bright because they live in the aryepiglottic / fold-surface space. Growl goes deeper — into the false folds — which is why it rolls and darkens rather than crunches.</div>
      <div class="vg-card-foot">CVT equivalent: Distortion (false/ventricular folds) or Growl (arytenoids against epiglottis) — audibly similar, mechanically distinct. Both supraglottic — true folds undamaged when done correctly. Key diagnostic: bright + forward = Grit or Rasp. Dark + deep + rolling = Growl.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Fry</div><div class="vg-card-id">TEX-fry</div>
      <div class="vg-card-body"><strong>Billie Eilish, Lana Del Rey, Chris Carrabba (Dashboard Confessional), Conor Oberst (Bright Eyes), early FKA twigs</strong> — Under-driven intimate creak. The opposite of overdrive: subglottic pressure is too low to sustain full phonation, so the folds approximate loosely and produce irregular, slow closure — a creaky, gravelly texture at the bottom of the pressure range. Inherently quiet: you cannot fry at volume. Raise the breath support and fry disappears, replaced by clean tone.
      <br><br>
      Fry is common in contemporary indie/bedroom-pop production as a deliberate intimacy signal — the voice at its most vulnerable and unguarded. It is also the default register most people drop into at the bottom of their range in conversation. The vocal fry of speech therapy and vocal coaching. Completely distinct from all overdrive distortions.</div>
      <div class="vg-card-foot">Goldilocks framework: Fry = pressure deficit (under-driven). Rasp/Grit/Growl/Scream = pressure excess (over-driven). Same vocal fold chaos, opposite cause. Diagnostic: can this distortion be sustained loudly? If yes → overdrive family. If it vanishes when the voice gets loud → Fry.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Scream</div><div class="vg-card-id">TEX-scream</div>
      <div class="vg-card-body"><strong>Bring Me The Horizon, Parkway Drive, Slipknot, Corey Taylor, Randy Blythe (Lamb of God), Spencer Chamberlain (Underoath)</strong> — Maximum overdrive: every structure engaged simultaneously at the highest sustainable (or unsustainable) subglottic pressure. Pitch is partially or fully abandoned. The ceiling of the distortion spectrum.
      <br><br>
      Anatomically the inverse of Fry — same fold-level chaos, opposite pressure situation. Scream is compressed fry driven past all thresholds: where Fry is the voice with too little air, Scream is the voice with too much. Healthy screaming technique uses the supraglottic structures (false folds, aryepiglottic narrowing) as a distortion buffer to protect the true folds — the same supraglottic engagement as Growl and Rasp, but at extreme pressure. Screaming without this buffer is what damages voices.</div>
      <div class="vg-card-foot">Pressure ceiling: Rasp → Grit → Growl → Scream (ascending overdrive). Scream vs. Growl: Growl retains pitch and has a specific tonal character; Scream loses pitch and engages everything. Scream vs. Fry: same fold chaos, opposite pressure direction.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Compression</div><div class="vg-card-id">TEX-compression</div>
      <div class="vg-card-body"><strong>Chris Cornell (Soundgarden), Eddie Vedder (Pearl Jam), Gavin Rossdale (Bush)</strong> (baritone wheelhouse) · <strong>Sting (The Police), Tony Lewis (The Outfield), Gotye, Matt Bellamy (Muse)</strong> (lyric tenor) · <strong>Maynard James Keenan (Tool)</strong> (dramatic tenor) — Pressed, glottal quality in clean delivery. The goldilocks zone at its upper limit: maximum fold adduction without crossing into overdrive territory. Every note feels weighted, held back slightly — the voice is doing more work than it appears to be.
      <br><br>
      The precursor to Grit: same tight adduction, but pressure remains controlled enough that tone stays clean. Reads very differently across voice weights — Sting's feels effortless; Maynard's feels like something enormous held back. Note: Maynard is an instructive case of compression doing the work of chest weight — strip the compression and the instrument is lighter than the Tool context suggests. Most singers do NOT have this quality — when in doubt, omit.</div>
      <div class="vg-card-foot">Goldilocks position: last stop before overdrive. Compression = clean zone maximum. Grit = first step over. Compression is glottal (fold-level), not pharyngeal — distinguish from TEX-modal-pinch (pharyngeal narrowing on high notes) and TEX-growl (false fold engagement).</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Modal Pinch</div><div class="vg-card-id">TEX-modal-pinch</div>
      <div class="vg-card-body"><strong>Bono (U2)</strong> (upper register) · <strong>Billy Corgan (Smashing Pumpkins)</strong> (full voice high notes) · <strong>Maynard James Keenan (Tool)</strong> (specific high passages) — Pharyngeal narrowing used as a coping mechanism at the top of modal range. The singer squeezes and narrows rather than transitioning into head voice, which lets them access higher pitches modally at the cost of that constricted, effortful, slightly desperate quality. A compensation strategy, not a stylistic choice.
      <br><br>
      Two varieties: <em>compensatory</em> (Bono, Maynard — situational, deployed specifically on high notes where the head voice transition would otherwise be required) and <em>habitual</em> (Corgan — simply where his voice lives at the top of his range).
      <br><br>
      Distinct from <strong>REG-witch</strong> (which is a full pharyngeal identity used throughout the range, not just on high notes) and from <strong>TEX-compression</strong> (which is glottal, not pharyngeal, and is present as a clean quality throughout — not specifically a high-note strategy). Also distinct from <strong>RES-mask-narrow</strong> (which is a deliberate resonance aesthetic applied from genuine head voice technique, not a modal compensation).</div>
      <div class="vg-card-foot">Diagnostic: "That sounds squeezed / constricted / like they're working hard to stay up there" → TEX-modal-pinch. "That voice just cuts with a raw pharyngeal pierce" → REG-witch. "That sounds nasal" → one of the RES-nasal-* tags.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Vibrato (Wide)</div><div class="vg-card-id">TEX-vibrato-wide</div>
      <div class="vg-card-body"><strong>Roy Orbison, Pavarotti, Amy Lee (Evanescence)</strong> — Slow, wide oscillation (~4–5Hz). Reads as theatrical, classical-adjacent, emotionally grand — the "big voice" signal even in pop contexts. The slow/wide operatic vibrato emerged from classical traditions where projection into large halls required wide oscillation. Only tag if vibrato is prominent and defining — many singers use some vibrato without it being a characteristic identity marker.</div>
      <div class="vg-card-foot">Vibrato rate is one of the most consequential tonal identity markers — the same underlying technique produces radically different audience perception depending on oscillation frequency. Wide and fast vibrato are perceptually distinct enough to warrant separate tags.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Vibrato (Fast)</div><div class="vg-card-id">TEX-vibrato-fast</div>
      <div class="vg-card-body"><strong>Stevie Wonder, Jeff Buckley</strong> (lower register) · <strong>Matt Bellamy (Muse), Freddie Mercury (Queen)</strong> (mid-speed, controlled — ~5–6Hz, transitional between wide and fast) — Fast, narrow oscillation (~6–8Hz+). Instrumental quality, closer to guitar tremolo. Does not read as trained or staged. More common in gospel, soul, and rock where amplification removes the projection requirement that drove classical wide vibrato.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Straight Tone</div><div class="vg-card-id">TEX-straight-tone</div>
      <div class="vg-card-body"><strong>Nick Drake, Elliott Smith, Thom Yorke (Radiohead), Maynard James Keenan (Tool), Rivers Cuomo (Weezer), Liam Gallagher (Oasis), Brandon Flowers (The Killers)</strong> — Deliberate, aesthetic absence of vibrato as an expressive statement. The stillness is the point. Must be a primary characteristic — many singers use little vibrato without making it a defining identity. For Maynard it functions as a contrast weapon against Tool's sonic density. Only tag when the straight tone is audibly intentional and consistent.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Breath / Smoke</div><div class="vg-card-id">TEX-breath-smoke</div>
      <div class="vg-card-body"><strong>Sade, Seal, Ray LaMontagne, John Mayer, Ed Roland (Collective Soul), Mariah Carey</strong> (verses/ballads) · <strong>Nat King Cole</strong> (slight coloring, not primary) · <strong>Steve Perry (Journey)</strong> (significant — breath woven into an otherwise pure head-resonance architecture) — Deliberate breath woven into tone as an expressive tool. Incomplete glottal closure allows air to leak through during each vibration cycle — the same phenomenon that causes untrained singers to run out of air, applied intentionally. The voice feels close, unhurried, lived-in. Distinct from Falsetto airiness (which is a byproduct of incomplete closure at the register level) — Breath/Smoke can be applied in Modal or Head Voice register as a deliberate color choice.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Cry</div><div class="vg-card-id">TEX-cry</div>
      <div class="vg-card-body"><strong>Whitney Houston, Sam Cooke, Adele, Marvin Gaye, Aretha Franklin, Ray Charles</strong> — Low larynx under emotional or intensity load, <em>without</em> raising the soft palate to close the nasal port. The forward/nasal resonance path stays open, so depth and cut coexist simultaneously — the voice sounds weighted and plaintive while retaining brightness. The perceptual signature is an aching, weighted, about-to-cry quality. The "boo-hoo" test: imitate fake melodramatic weeping and sustain a note — that laryngeal configuration is TEX-cry.
      <br><br>
      Strongly associated with <strong>gospel-lineage R&B and soul</strong>, where singing from a place of emotional weight is foundational to the tradition. Distinguished from TEX-covered: Covered raises the soft palate and closes the nasal port, removing the cut entirely. Cry does not close the nasal port — it is depth without darkness. A singer can be simultaneously bright (nasal/forward resonance) and crying.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Covered</div><div class="vg-card-id">TEX-covered</div>
      <div class="vg-card-body">See full description under Resonance section above. As a Texture tag: low larynx plus raised soft palate — the nasal port is closed and brightness is removed. The darkness is uniform throughout, not emotionally loaded. Distinguished from TEX-cry: if the voice has depth but still carries cut and brightness, it is Cry not Covered. Covered + Mask/Chest (Luther Vandross) produces a particularly distinctive warmth. Covered + Head Voice (classical operatic tenor) produces the ringing warmth of voce coperta. Because nasal resonance and covered quality are inversely proportional, Covered functions as a natural limiter on Mask placements — full covered commitment progressively reduces mask resonance toward zero.</div>
    </div>

  </div>

  <!-- ═══ PHRASING ══════════════════════════════════════════════════════════ -->
  <div class="vg-cat" id="tech-phrasing" style="--vc:var(--phr)">
    <div class="vg-cat-label">Phrasing · Stylistic Motor Patterns</div>

    <div class="vg-card">
      <div class="vg-card-name">Speak-Sing</div><div class="vg-card-id">PHR-speak-sing</div>
      <div class="vg-card-body"><strong>Leonard Cohen, Lou Reed, early David Bowie, Rex Harrison</strong> — Operating in speech motor mode: casual variable cord closure, reduced breath support, minimal sustained tone. The singer is not executing singing technique — they are applying spoken delivery to pitched material. Can co-occur with any resonance placement depending on where the individual's natural speaking voice lives. Cohen's speak-sing sits over Chest resonance; Bowie's in his early work over Mask Neutral. Reserved for singers where this is a primary mode — not incidental spoken-word passages in otherwise sung material.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Runs &amp; Riffs</div><div class="vg-card-id">PHR-runs-riffs</div>
      <div class="vg-card-body"><strong>Mariah Carey, Whitney Houston, Beyoncé, Christina Aguilera, Stevie Wonder, Wanya Morris (Boyz II Men)</strong> — Melodic improvisation and ornamentation moving through diatonic scale degrees. Runs through scales and arpeggios; riffs between pitches. Distinct from Microtonal which moves through intervals outside Western equal temperament.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Portamento</div><div class="vg-card-id">PHR-portamento</div>
      <div class="vg-card-body"><strong>Smokey Robinson, Marvin Gaye, Sam Cooke, Ron Isley, Frank Sinatra, Tony Bennett, Nat King Cole, Michael Bublé</strong> — Intentional, stylistic sliding between pitches where the slide itself is the expressive point. The defining ornament of both classic soul and the crooner tradition. Not normal pitch connection — portamento is when the movement between notes is audible, deliberate, and aesthetically central to the delivery.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Grunt</div><div class="vg-card-id">PHR-grunt</div>
      <div class="vg-card-body"><strong>James Brown, Michael Jackson, Bruno Mars</strong> — Short, staccato, pressurized exclamation used as rhythmic punctuation between or within phrases. The physical percussive attack of gospel, soul, and R&amp;B delivery — a "HUH" or "OW" that functions as a rhythmic instrument rather than a melodic one. Not sustained, not textural — purely punctuation.</div>
      <div class="vg-card-foot">CVT term: Grunt — whole supraglottic structure with large amplitude vibration. Anatomically distinct from Growl and Fry despite all three being supraglottic phenomena.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Microtonal</div><div class="vg-card-id">PHR-microtonal</div>
      <div class="vg-card-body"><strong>Cheb Mami, Umm Kulthum, Fairuz, Amr Diab</strong> — Rapid ornamental runs through microtonal intervals (quarter tones and neutral tones) that sit outside Western equal temperament. Rooted in the maqam traditions of North Africa and the Middle East. The technique heard on Sting's Desert Rose (Cheb Mami) is the most recognizable Western pop exposure. Distinct from R&amp;B Runs &amp; Riffs which move through diatonic scale degrees.</div>
      <div class="vg-card-foot">The underlying modal system: maqam (Arabic), makam (Turkish), dastgah (Persian) — related traditions across the region.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Rapid Fire</div><div class="vg-card-id">PHR-rapid-fire</div>
      <div class="vg-card-body"><strong>Eminem, Busta Rhymes</strong> — Extreme syllabic velocity with precise articulation maintained throughout. Rhythm and motor precision are the technique. The musical content is in the rhythmic density, not the pitch.</div>
    </div>

    <div class="vg-card">
      <div class="vg-card-name">Yodel / Break</div><div class="vg-card-id">PHR-yodel</div>
      <div class="vg-card-body"><strong>Jewel, LeAnn Rimes, Hank Williams, Dolores O'Riordan, Bon Iver</strong> — A deliberate, audible flip between registers (chest/modal ↔ head/falsetto) used as an ornament — the break is exposed on purpose, the opposite of a smooth passaggio. Distinct from an involuntary untrained crack (a failure, not a choice). The discrete-flip cousin of Portamento, which glides smoothly where the yodel jumps and flips.</div>
    </div>

  </div>

  <div class="footer">Building A Rockstar Voice Technique Guide v4.99885 · May 2026 · 36 techniques</div>
</div>
          </div><!-- end vg-toc-content -->
        </div><!-- end vg-toc-layout -->
      </div>

      <!-- PAGE: PRACTICE & EXERCISES -->
      <div id="vgp-practice" class="vg-page" style="display:none;">
        <!-- 🎹 Pitch Keyboard — an active drill tool, so it lives at the top of
             Practice (moved here from the Vox Gym root, 2026-06-09). togglePiano()
             opens the self-contained #piano-sheet. -->
        <button class="btn btn-secondary vg-prac-piano" onclick="togglePiano()" style="width:100%;justify-content:center;margin-bottom:14px;">🎹 Open Pitch Keyboard</button>
        <div class="vg-toc-layout">
          <div class="vg-toc-pane" id="vgtoc-practice">
            <div class="vg-toc-inner">
              <span class="vg-toc-section-label">Practice</span>
              <a class="vg-toc-link" onclick="vgTocGo('vg-warmup')">Warm-Up</a>
              <a class="vg-toc-link" onclick="vgTocGo('vg-faults')">Fault Correction</a>
              <a class="vg-toc-link" onclick="vgTocGo('vg-exercises')">Vocal Foundations</a>
              <a class="vg-toc-link" onclick="vgTocGo('prac-technique')">Technique Builder</a>
              <a class="vg-toc-link" onclick="vgTocGo('vg-reading')">Reading</a>
            </div>
          </div>
          <div class="vg-toc-content">

        <!-- Piano nudge -->
        <div id="vg-piano-nudge" style="display:flex;align-items:center;gap:10px;padding:10px 14px;background:rgba(124,58,237,0.08);border:1px solid rgba(124,58,237,0.25);border-radius:var(--radius);margin-bottom:16px;">
          <span style="font-size:22px;">🎹</span>
          <div style="flex:1;min-width:0;">
            <div style="font-size:12px;font-weight:700;color:#a78bfa;margin-bottom:1px;">Reference Piano Available</div>
            <div style="font-size:11px;color:var(--text3);line-height:1.5;">Use the <strong style="color:var(--text2);">🎹</strong> button (bottom-right) to open a pitch reference keyboard any time during practice.</div>
          </div>
          <button onclick="document.getElementById('vg-piano-nudge').style.display='none'" style="background:none;border:none;color:var(--text3);font-size:16px;cursor:pointer;flex-shrink:0;">✕</button>
        </div>

        <!-- WARM-UP -->
        <div id="vg-warmup" style="margin-bottom:24px;">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:1px;color:var(--text2);margin-bottom:4px;">🔥 Warm-Up</div>
          <div style="font-size:12px;color:var(--text3);margin-bottom:14px;line-height:1.6;">Do this before any other practice. Two versions — pick based on how much time you have.</div>

          <div class="ex-card" style="border-left:3px solid var(--text3);">
            <div class="ex-title">What the science actually says</div>
            <div class="ex-body">
              <p>Vocal warm-up research is <strong>mixed but leans weakly positive</strong>. Studies (Elliot/Sundberg, Amir, McHenry, and others) show modest improvements: slightly increased range, lower jitter, increased blood flow to the folds, and reduced perceived effort. A 2017 systematic review found small effect sizes across the literature.</p>
              <p><strong>Subjective benefits are bigger than objective ones</strong> — singers consistently report feeling readier and less effortful after warming up, even when the measurements show only small changes. The benefit is larger for untrained voices, older voices, and demanding performances (long sets, belt-heavy material). It's smaller for trained voices that are already "in shape" and for short casual sets.</p>
              <p>Most karaoke nights aren't pushing the voice hard enough to demand a full warm-up — but the <em>tail risk</em> case still applies: warming up reduces the chance of strain on a high-demand song, even when average benefit is small. If you're about to belt a 3-minute Whitney number cold, even 90 seconds of light phonation is meaningfully protective.</p>
              <p>The bottom line: <strong>warm up if you can. It probably helps a little, very rarely hurts, and your voice will thank you on the demanding stuff.</strong></p>
            </div>
          </div>

          <div class="ex-card" style="border-left:3px solid var(--success);">
            <div class="ex-title">⚡ Quick Warm-Up (3–5 min)</div>
            <div class="ex-body">
              <p>The minimum effective dose. Do this in the car on the way to the bar, in the bathroom mirror, or sitting at your table before your name gets called.</p>
              <p><strong>1. Lip trills on a 5-note scale (1 min).</strong> Loose lips, let air flow through and make them bubble. Start at a comfortable low pitch (try middle C for men, an octave up for women — use the 🎹 button to find it). Slide up through five notes, then back down. Repeat 4–5 times moving up and down by half-steps. Lip trills are an SOVT (semi-occluded vocal tract) exercise — they balance airflow and fold closure without strain.</p>
              <p><strong>2. Sirens on "ng" (1–2 min).</strong> Make the "ng" sound at the end of "sing" — the tongue lifts to the soft palate. Slide continuously from your lowest comfortable note up to your highest and back, like a smooth siren. 5–6 round trips. Don't stop at any note. The goal is unbroken movement through your full range — this coordinates chest and head voice.</p>
              <p><strong>3. Light "Mum-mum-mum" on a 5-note scale (1–2 min).</strong> Hum lightly with closed lips, then add "M-um" syllables on a slow 5-note scale up and down. Keep it gentle — you're warming up, not performing. Repeat at progressively higher starting pitches by half-steps.</p>
              <p><strong>Done.</strong> Voice should feel readier and lighter than when you started. If anything feels tight or sore, stop — that's a sign of a deeper issue, not something a warm-up can fix.</p>
            </div>
          </div>

          <div class="ex-card" style="border-left:3px solid var(--accent);">
            <div class="ex-title">🔥 Full Warm-Up (10–15 min)</div>
            <div class="ex-body">
              <p>For demanding performances, long sets, or any night you plan to belt. Do the three quick warm-up exercises first, then add these:</p>
              <p><strong>4. Physical prep (1 min, before any sound).</strong> Roll shoulders back and down. Drop the jaw open and let it hang loose. Gently massage the muscles at the jaw hinge and the sides of the neck. Stick the tongue out and stretch it side to side. Yawn fully — feel the larynx drop and the soft palate lift. This isn't optional fluff: tense external muscles are the #1 source of strain.</p>
              <p><strong>5. Breath ladder (1–2 min).</strong> Inhale slowly through the nose for a 4-count, expanding into the lower ribs and belly (not the chest). Hiss out through teeth for a steady 8-count, keeping the volume even from start to end. Repeat, extending the hiss to 12, then 16, then 20 counts. The exhale should feel controlled, not collapsed. This activates breath support before you ask the voice to do anything athletic.</p>
              <p><strong>6. Octave glides on "Wee" (2–3 min).</strong> Pick a comfortable starting note. Glide smoothly up an octave on "Wee" — the lips slightly forward, almost a whistle shape — and back down. The "W" onset encourages head-voice engagement; the "ee" vowel keeps the larynx free. 4–5 reps, moving the starting pitch up by half-steps each time. Don't muscle through register changes — let the voice flip if it wants to. Smooth out comes with reps, not force.</p>
              <p><strong>7. Vowel sequence on a sustained note (1–2 min).</strong> Pick a mid-range note. Sustain it through the vowel sequence "Ee → Ay → Ah → Oh → Oo," changing the vowel without re-articulating the pitch. Each vowel shifts the resonance — feel where the sound lives in your face/throat for each one. This builds vowel-shape awareness, which is the foundation of consistent tone across a song.</p>
              <p><strong>8. Light articulation work (1–2 min).</strong> Speak this aloud at performance volume: "Red leather, yellow leather" 5×. "Unique New York" 5×. "Toy boat" 5×. Then sing a simple 5-note scale on "Mee-Ay-Mah-Oh-Moo." Articulation muscles need their own activation — it's why "first song of the night" often feels mushy even when the rest of the voice is ready.</p>
              <p>Total: 10–15 minutes. After this you can comfortably sing for an hour or more without re-warming.</p>
            </div>
          </div>

          <div class="ex-card" style="border-left:3px solid var(--warning);">
            <div class="ex-title">Important caveats</div>
            <div class="ex-body">
              <p><strong>Warm-up ≠ rescue.</strong> If your voice is hoarse, sore, or fatigued before you start, no warm-up will fix that. Skip the bar that night.</p>
              <p><strong>Don't over-warm.</strong> Singing scales for 30 minutes before a 3-song karaoke set will fatigue you more than help. Match the warm-up to the demand.</p>
              <p><strong>Hydration matters more than warm-up.</strong> Water at least 30 minutes before singing (it takes time to reach the vocal folds — they're not directly hydrated by what you swallow). Dehydrated folds + warm-up still beat dehydrated folds + no warm-up, but hydrated folds + no warm-up beats both.</p>
              <p><strong>If anything hurts, stop.</strong> Warm-ups should feel easy. Pain or strain means something's wrong with the technique itself — that's a Fault Correction problem, not a warm-up problem.</p>
            </div>
          </div>
        </div>

        <!-- FAULT CORRECTION -->
        <div id="vg-faults" style="margin-bottom:24px;">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:1px;color:var(--text2);margin-bottom:4px;">🔧 Fault Correction</div>
          <div style="font-size:12px;color:var(--text3);margin-bottom:14px;line-height:1.6;">Common technique problems and how to fix them. Start here if something specific isn't working.</div>

          <div class="ex-card">
            <div class="ex-title">Breathiness / Air Leak</div>
            <div class="ex-body">
              <p><strong>What it is:</strong> Excess air in the tone — the vocal folds aren't fully approximating. The sound has a smoky, airy quality even when you want a clean tone. Distinct from intentional breathiness (a stylistic choice).</p>
              <p><strong>Causes:</strong> Incomplete fold closure, insufficient subglottic pressure, or a habit formed by singing too quietly for too long.</p>
              <p><strong>Fix:</strong> Onset exercises — sing "Hee" cleanly, focusing on a simultaneous, gentle glottal engagement with no puff of air before the tone. Try the "Hiss-to-Hum" transition: sustain "sss," then without stopping, add pitch to arrive at a hum. The hum should be clean. Lip trills with slightly firmer core engagement also encourage efficient fold closure without squeezing.</p>
              <p><strong>Progress check:</strong> Record a quiet sustained "Ee" vowel. Clean phonation is tone-dominant; breathiness has audible air mixed in.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Pressed Tone / Glottal Attack</div>
            <div class="ex-body">
              <p><strong>What it is:</strong> Too much fold adduction creates a hard, pressed, or strangled quality. Every phrase starts with an audible glottal pop or click.</p>
              <p><strong>Causes:</strong> Excess subglottic pressure, throat tension, over-correcting for breathiness.</p>
              <p><strong>Fix:</strong> Aspirate onset practice — begin every note with a soft "H" before the vowel ("Hah," "Hee," "Hoh"). The H should blend into the vowel rather than being a deliberate puff. Over time, reduce the H until the onset is clean and simultaneous. Sighing exercises also help: let out a sigh on a sustained pitch — the tone comes in easily without pressing.</p>
              <p><strong>Progress check:</strong> Speak a phrase, then sing the same phrase. If speaking feels easier and less forced, the pressed quality is coming from your singing posture. Bring the ease of speech into singing.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Register Break / Flip</div>
            <div class="ex-body">
              <p><strong>What it is:</strong> An abrupt, uncontrolled switch between chest and head voice at the passaggio — the voice cracks, drops out, or "flips" audibly. Almost universal in singers who haven't addressed register coordination.</p>
              <p><strong>Causes:</strong> The TA and CT muscles aren't yet coordinating smoothly through the passaggio. The voice defaults to a hard switch instead of a blend.</p>
              <p><strong>Fix:</strong> Siren exercises are the most effective — slide continuously from bottom to top and back on "ng" without staying in either register. The goal is smooth, unbroken movement. Also: slow 5-note scales crossing the passaggio on "Ooh." Don't avoid the break — go through it repeatedly at slow tempo until the transition smooths out. Reducing volume at the passaggio is normal; gradually add weight back as coordination improves.</p>
              <p><strong>Progress check:</strong> Record a scale that crosses your passaggio. Identify the exact note(s) where the break happens — that note is your training target.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Laryngeal Tension / Throat Squeeze</div>
            <div class="ex-body">
              <p><strong>What it is:</strong> External laryngeal muscles are clenching around the larynx instead of letting the intrinsic muscles do their work. The voice sounds strangled or tight, especially in the upper range. Singing feels physically effortful.</p>
              <p><strong>Causes:</strong> Compensating for insufficient breath support, reaching for high notes by lifting the larynx with external muscles, anxiety and tension, singing too loudly too soon.</p>
              <p><strong>Fix:</strong> Yawn-sigh exercises — yawn fully (the larynx drops and opens naturally), then on the exhale sigh a pitch. Practice sustaining the open, yawned space on actual notes. Gently massage the muscles around the jaw hinge and sides of the neck before practice. Reduce volume — tension often hides at high volume. Sing passages that feel strained at a quiet enough dynamic that the strain disappears, then gradually bring the volume back.</p>
              <p><strong>Progress check:</strong> Place fingers lightly on the sides of your larynx. It should not feel gripped or that the muscles around it are cord-like.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Pitch Instability / Wobble</div>
            <div class="ex-body">
              <p><strong>What it is:</strong> Unable to sustain a steady pitch — the note wavers, drifts, or fluctuates noticeably. Distinct from intentional vibrato (which is controlled and regular).</p>
              <p><strong>Causes:</strong> Inconsistent breath support, tension in the vibrato system creating an exaggerated wobble, lack of ear training, or fatigue.</p>
              <p><strong>Fix:</strong> Sustained vowel exercises against a drone — sustain "Ah" against a held piano note for 8 beats and listen carefully. Any drift is immediately audible against the reference pitch. Core engagement drill: sustain a note while gently pressing your hands into your sides just below the ribs. If the pitch steadies, breath support was the issue. For wobble specifically (too-slow oscillation), check for held tension — wobble is often vibrato running too slow because the muscles are too tight.</p>
              <p><strong>Progress check:</strong> Use a tuner app to display your pitch in real time while sustaining. The needle should barely move on a steady tone.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Nasality Imbalance</div>
            <div class="ex-body">
              <p><strong>What it is:</strong> Too much or too little nasal resonance. Excess nasality creates a honky, pinched quality on all vowels. Insufficient nasality can make the voice sound overly dark or muffled.</p>
              <p><strong>Causes:</strong> Soft palate not rising consistently (excess nasality) or raised too aggressively (insufficient). Regional speech habits often carry into singing.</p>
              <p><strong>Fix (excess nasality):</strong> Soft palate lift — sing "Nee" (naturally nasal), then raise your soft palate as if beginning a yawn and sing "Nee" again. The tone should open up. Practice the nose-pinch test: if pinching your nose during a sustained "Ah" changes the tone dramatically, excess nasality is confirmed. Work on reducing that difference.</p>
              <p><strong>Fix (too dark / insufficient):</strong> Hum exercises — hum on "Mmm" and feel the vibration in your lips and face. Open slowly to "Mah" while maintaining the forward buzz. The goal is carrying nasal resonance into open vowels.</p>
            </div>
          </div>
        </div>

        <div style="border-top:1px solid var(--border);margin-bottom:24px;"></div>

        <!-- VOCAL FOUNDATIONS -->
        <div id="vg-exercises">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:1px;color:var(--text2);margin-bottom:4px;">🏋️ Vocal Foundations</div>
          <div style="font-size:12px;color:var(--text3);margin-bottom:14px;line-height:1.6;">Exercises are written for piano. If you have another instrument, use it — the principles are the same. No instrument? Open the reference piano via the 🎹 button anytime.</div>

          <!-- Exercise category tabs -->
          <div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px;" id="ex-cat-tabs">
            <button class="vg-nav-btn active" onclick="showExCat('foundation',this)">Foundation</button>
            <button class="vg-nav-btn" onclick="showExCat('register',this)">Register</button>
            <button class="vg-nav-btn" onclick="showExCat('resonance',this)">Resonance</button>
            <button class="vg-nav-btn" onclick="showExCat('texture',this)">Texture</button>
            <button class="vg-nav-btn" onclick="showExCat('phrasing',this)">Phrasing</button>
          </div>

          <div id="ex-pages">

            <!-- FOUNDATION EXERCISES -->
            <div id="exp-foundation" class="ex-page">
              <div style="font-size:11px;color:var(--text3);line-height:1.6;padding:8px 12px;background:rgba(255,255,255,0.02);border:1px solid var(--border);border-radius:8px;margin-bottom:14px;">These exercises build the physical and acoustic foundations that all other technique depends on — breath support, onset, resonance awareness, and passaggio navigation. Work through these before focusing on specific techniques.</div>

              <div class="ex-card">
                <div class="ex-title">1. Lip Trill (Bubble) — Breath & Support</div>
                <div class="ex-body">
                  <p>Place your fingers lightly on your cheeks. Blow air through loosely closed lips until they vibrate ("brrr"). Once you have a consistent trill, add pitch.</p>
                  <p><strong>At the piano:</strong> Play a 5-note scale (C–D–E–F–G–F–E–D–C) on a comfortable pitch. Trill through each note. Move the scale up by a half-step, trill again. Repeat ascending until you feel strain, then descend back. Aim for C3–C5 range over time.</p>
                  <p><strong>What to feel:</strong> Your ribs should stay expanded. Your belly should move — not your chest. If the trill stops, you're gripping or pushing air. Ease the pressure and let the breath flow.</p>
                  <p><strong>What it trains:</strong> Consistent subglottal pressure, body breathing, relaxed phonation onset.</p>
                </div>
              </div>

              <div class="ex-card">
                <div class="ex-title">2. Humming — Resonance Placement</div>
                <div class="ex-body">
                  <p>Hum on a comfortable mid-range pitch (around E3–G3 for most voices). Feel the vibration in your lips, teeth, and face — not deep in your throat.</p>
                  <p><strong>At the piano:</strong> Play a 5-note scale and hum through it. Then try an octave jump — hum down a note, then up an octave. Notice where the hum wants to "sit" in your face. That buzzing sensation is forward resonance.</p>
                  <p><strong>What to feel:</strong> Tingling in the mask of the face (cheekbones, around the nose, upper lip). If you feel it only in your throat, you're pulling down. Raise your soft palate slightly — as if you just sniffed something pleasant.</p>
                  <p><strong>What it trains:</strong> Forward resonance placement, mask awareness, smooth register transitions.</p>
                </div>
              </div>

              <div class="ex-card">
                <div class="ex-title">3. Onset Exercise — Clean Attack</div>
                <div class="ex-body">
                  <p>Sing the syllable "Hee" on a single pitch. The "H" should blend smoothly into the vowel — no explosion of air at the start (aspirate onset) and no hard glottal click (glottal onset). The ideal is a simultaneous, easy onset.</p>
                  <p><strong>At the piano:</strong> Play a single note (start at E3 or F3). Sing "Hee" cleanly. Hold it for 3–4 beats. Move up a half-step and repeat. Go up to about the top of your comfortable range, then back down. Try with "Hoh" and "Hah" as well.</p>
                  <p><strong>What to feel:</strong> The beginning of the tone should feel like a gentle "click" into resonance — no puff of air before it, no grunt. If you hear a hard attack, add a tiny aspirate to soften; if you hear a breathy onset, engage your core slightly more.</p>
                  <p><strong>What it trains:</strong> Glottal closure efficiency, breath-to-phonation ratio, tonal clarity.</p>
                </div>
              </div>

              <div class="ex-card">
                <div class="ex-title">4. Siren — Full Range & Passaggio Navigation</div>
                <div class="ex-body">
                  <p>On an "ng" sound (as in "sing"), slide continuously from the bottom of your range to the very top and back down — like a siren. Don't try to stay in chest voice or head voice; let the transition happen naturally.</p>
                  <p><strong>At the piano:</strong> No specific notes needed for sirens — the point is continuous, unbroken movement. But you can use the piano to identify your passaggio: the note where your voice wants to flip or thin out. For most male voices this is around E4–G4; for female voices around D5–F5. Play that note and practice crossing through it on the siren.</p>
                  <p><strong>What to feel:</strong> A smooth, blended transition — not a gear-change. On the way up, don't push chest; on the way down, don't drop into a different gear. Think "one voice."</p>
                  <p><strong>What it trains:</strong> Passaggio coordination, register blending, laryngeal flexibility.</p>
                </div>
              </div>

              <div class="ex-card">
                <div class="ex-title">5. Sustained Vowel — Tone Consistency</div>
                <div class="ex-body">
                  <p>Sing a single pitch on an "Ah" vowel (as in "father") and hold it for 8 full beats. The tone should be even — no wavering, no crescendo, no fading at the end.</p>
                  <p><strong>At the piano:</strong> Play a comfortable note (around C4 for most voices). Sing "Ah" and hold. Use a metronome or count 8 slow beats. Try again on a 5th above (G4). Then come back down.</p>
                  <p><strong>What to feel:</strong> A steady stream of support — as if you're slowly pressing your back against a wall throughout the note. No pumping. No sagging. The end of the note should feel the same as the beginning.</p>
                  <p><strong>What it trains:</strong> Breath management, support consistency, tonal evenness.</p>
                </div>
              </div>
            </div>

            <!-- REGISTER EXERCISES -->
            <div id="exp-register" class="ex-page" style="display:none;">
              <div style="font-size:11px;color:var(--text3);line-height:1.6;padding:8px 12px;background:rgba(56,189,248,0.05);border:1px solid rgba(56,189,248,0.2);border-radius:8px;margin-bottom:14px;">These exercises develop control over each laryngeal register — modal, head, belt, falsetto, and beyond. Each is a distinct physical configuration; the goal is to be able to access any of them intentionally and transition between them smoothly.</div>

              <div class="ex-card ex-reg">
                <div class="ex-tag" style="color:#38bdf8;border-color:rgba(56,189,248,0.4);">REG · Modal/Speech</div>
                <div class="ex-title">Speak-to-Sing Transition</div>
                <div class="ex-body">
                  <p>Speak a phrase in your normal voice, then sing the same phrase on a mid-range note. The two should feel like the same instrument — same resonance, same ease.</p>
                  <p><strong>At the piano:</strong> Play a note around your speaking pitch (for most people, E3–A3). Say "One two three four" naturally, then sing those words on that pitch. Move up one step. Repeat. If it starts to feel "singerly" or forced, you've left modal — come back down.</p>
                  <p><strong>What it trains:</strong> Speech-level phonation, efficient modal registration, natural onset.</p>
                </div>
              </div>

              <div class="ex-card ex-reg">
                <div class="ex-tag" style="color:#38bdf8;border-color:rgba(56,189,248,0.4);">REG · Head Voice</div>
                <div class="ex-title">Head Voice Descent</div>
                <div class="ex-body">
                  <p>Start at the top of your range where you know you're in head voice. Sing "Ooh" (as in "moon") on a high note and hold it, then slowly descend a scale. Keep the lightness and openness of head voice all the way down — don't let it "thicken" into chest prematurely.</p>
                  <p><strong>At the piano:</strong> Start at C5 (or wherever feels like clear head voice for your voice type). Descend: C5–B4–A4–G4–F4–E4–D4–C4. Sing "Ooh" through all 8 notes without breaking or switching registers.</p>
                  <p><strong>What it trains:</strong> Sustained head register awareness, resistance to register pulling, upper range ease.</p>
                </div>
              </div>

              <div class="ex-card ex-reg">
                <div class="ex-tag" style="color:#38bdf8;border-color:rgba(56,189,248,0.4);">REG · Belt</div>
                <div class="ex-title">Belt Anchoring</div>
                <div class="ex-body">
                  <p>A belt is an extended, supported chest-dominant phonation — not just singing loudly. The key is firm body support with a raised soft palate and an open throat, not a squeezed throat.</p>
                  <p><strong>At the piano:</strong> Play E4. Sing "Hey!" with the energy of calling across a room — firm, bright, supported. Don't push from the throat; push from your core. Repeat on F4, then G4 if comfortable. Don't go higher without a solid foundation on E4 first.</p>
                  <p><strong>What to feel:</strong> Engagement in your lower abdomen and back. Your throat should feel open, not clenched. The sound should be forward, not pushed backward.</p>
                  <p><strong>What it trains:</strong> Belt configuration, support engagement, open-throat phonation under load.</p>
                </div>
              </div>

              <div class="ex-card ex-reg">
                <div class="ex-tag" style="color:#38bdf8;border-color:rgba(56,189,248,0.4);">REG · Falsetto</div>
                <div class="ex-title">Falsetto Isolation</div>
                <div class="ex-body">
                  <p>Falsetto is a disconnected, light upper register — the cords are not fully approximating. It should feel easy and ethereal, not strained.</p>
                  <p><strong>At the piano:</strong> Play G4–C5. Sing "Ooh" very lightly — almost like a gentle hoot. Let the tone be thin and breathy rather than trying to make it full. Once you have it, try to gradually reduce the breathiness while keeping the lightness.</p>
                  <p><strong>What it trains:</strong> Falsetto access and control, upper register isolation, register awareness.</p>
                </div>
              </div>

              <div class="ex-card ex-reg">
                <div class="ex-tag" style="color:#38bdf8;border-color:rgba(56,189,248,0.4);">REG · Witch Voice</div>
                <div class="ex-title">Pharyngeal Narrowing</div>
                <div class="ex-body">
                  <p>The "witch voice" or twang is a pharyngeal constriction that creates a bright, cutting, slightly nasal quality. Think of the "witch" from a cartoon, or the "nyah nyah" sound children make.</p>
                  <p><strong>At the piano:</strong> Play E4–B4. Sing "Nyah" on each note. The sound should be highly focused and a bit obnoxious. Once you have the quality, try to channel the same pharyngeal shape into a more musical vowel like "Ah" or "Oh."</p>
                  <p><strong>What it trains:</strong> Pharyngeal constriction, twang/edge, upper modal power without pushing.</p>
                </div>
              </div>

              <div class="ex-card ex-reg">
                <div class="ex-tag" style="color:#38bdf8;border-color:rgba(56,189,248,0.4);">REG · Whistle</div>
                <div class="ex-title">Whistle Register Approach</div>
                <div class="ex-body">
                  <p>The whistle register is above high C (C6 for sopranos). It is not accessible for all voices and should never be forced. It requires extreme cord tension with minimal breath pressure.</p>
                  <p><strong>Approach:</strong> Sustain a clear, quiet falsetto on C5. Slowly ascend — D5, E5, F5 — keeping the sound as small and focused as possible. Some voices will find a second "flip" into whistle around G5–C6. Don't push for it; let it emerge. If it doesn't, your whistle register may simply not be activated yet.</p>
                  <p><strong>What it trains:</strong> Extreme upper register access, minimal pressure phonation, cord tension awareness.</p>
                </div>
              </div>
            </div>

            <!-- RESONANCE EXERCISES -->
            <div id="exp-resonance" class="ex-page" style="display:none;">
              <div style="font-size:11px;color:var(--text3);line-height:1.6;padding:8px 12px;background:rgba(167,139,250,0.05);border:1px solid rgba(167,139,250,0.2);border-radius:8px;margin-bottom:14px;">Resonance is where your sound lives in your body — not where you produce it, but where it amplifies and colors. These exercises develop awareness of the nasal, mask, chest, and head resonating spaces, and how to route your tone intentionally.</div>

              <div class="ex-card ex-res">
                <div class="ex-tag" style="color:#a78bfa;border-color:rgba(167,139,250,0.4);">RES · Mask (Sweet)</div>
                <div class="ex-title">Forward Placement — "Mah-Mee"</div>
                <div class="ex-body">
                  <p>The mask is the forward, facial resonating space — cheekbones, sinuses, upper lip area. Sweet mask resonance (think Michael Jackson, Bruno Mars) is bright and present without being harsh or nasal.</p>
                  <p><strong>At the piano:</strong> Play a 5-note scale (C–D–E–F–G). Sing "Mah-Mee-Mah-Mee-Mah" across the 5 notes. The "M" onset will naturally encourage forward placement. Focus on keeping the buzz in your face, not your throat. Try to carry the "Mee" brightness back into the "Mah."</p>
                  <p><strong>What it trains:</strong> Forward resonance, mask engagement, brightness without nasality.</p>
                </div>
              </div>

              <div class="ex-card ex-res">
                <div class="ex-tag" style="color:#a78bfa;border-color:rgba(167,139,250,0.4);">RES · Nasal (Sweet vs Harsh)</div>
                <div class="ex-title">Nasal Spectrum Control</div>
                <div class="ex-body">
                  <p>Nasality is not all bad — controlled nasal resonance adds brightness and character. The difference between "sweet" nasal (Rivers Cuomo, early JT) and "harsh" nasal (Michael Stipe) is largely a matter of soft palate height and laryngeal position.</p>
                  <p><strong>Exercise:</strong> Sing "Nee" on a mid-range pitch. Notice the strong nasal component. Now raise your soft palate as if beginning a yawn and sing "Nee" again — the tone should open up and brighten without the pinched nasal quality. Practice moving between these two deliberately.</p>
                  <p><strong>At the piano:</strong> Play E3–E4 (or wherever you're comfortable). Alternate between "closed nasal" Nee and "open" Nee on the same note 4 times each, then move up a step.</p>
                  <p><strong>What it trains:</strong> Soft palate control, nasal resonance calibration, tonal color awareness.</p>
                </div>
              </div>

              <div class="ex-card ex-res">
                <div class="ex-tag" style="color:#a78bfa;border-color:rgba(167,139,250,0.4);">RES · Open Chest</div>
                <div class="ex-title">Chest Resonance — "Boh"</div>
                <div class="ex-body">
                  <p>Open chest resonance (Bruce Springsteen, John Mellencamp) is oral-dominant and full-bodied — minimal nasality, strong chest sensation.</p>
                  <p><strong>At the piano:</strong> Play C3–G3 (lower range). Sing "Boh" (as in "boat") on each note. Place one hand on your sternum — you should feel vibration. Keep the sound open and unforced. Don't try to make it louder; try to make it more resonant.</p>
                  <p><strong>What it trains:</strong> Chest cavity resonance, oral vowel openness, low resonance awareness.</p>
                </div>
              </div>

              <div class="ex-card ex-res">
                <div class="ex-tag" style="color:#a78bfa;border-color:rgba(167,139,250,0.4);">RES · Head/Mask (Chiaroscuro)</div>
                <div class="ex-title">Ring & Depth — "Voh-Vee"</div>
                <div class="ex-body">
                  <p>The trained chiaroscuro (Italian: light-dark) combines head/mask brightness with chest depth — the classical "ring" quality. This is the resonance of fully trained voices.</p>
                  <p><strong>At the piano:</strong> Play G3–G4. Sing "Voh" (dark) followed immediately by "Vee" (bright) on the same pitch. Try to blend the two — the brightness of the "Vee" into the depth of the "Voh." The V-consonant encourages forward placement.</p>
                  <p><strong>What it trains:</strong> Resonance balance, chiaroscuro awareness, full voice ring.</p>
                </div>
              </div>
            </div>

            <!-- TEXTURE EXERCISES -->
            <div id="exp-texture" class="ex-page" style="display:none;">
              <div style="font-size:11px;color:var(--text3);line-height:1.6;padding:8px 12px;background:rgba(244,114,182,0.05);border:1px solid rgba(244,114,182,0.2);border-radius:8px;margin-bottom:14px;">Texture is what your voice does on top of its register and resonance — grit, vibrato, breathiness, compression, and more. These exercises are about controlled addition of color, not damage. Always work from a place of ease, not force.</div>

              <div class="ex-card ex-tex">
                <div class="ex-tag" style="color:#f472b6;border-color:rgba(244,114,182,0.4);">TEX · Vibrato</div>
                <div class="ex-title">Vibrato Development — Trill & Release</div>
                <div class="ex-body">
                  <p>Natural vibrato is a slight, regular oscillation of pitch (typically 5–7 Hz) produced by relaxed, coordinated phonation — not a forced wobble. It cannot really be "added"; it emerges when tension is released.</p>
                  <p><strong>Exercise A (Release):</strong> Sustain a note on "Ah." Push slightly, then consciously release the push mid-note. Notice if vibrato appears. Many singers find vibrato emerges when they stop trying to hold a "straight" tone.</p>
                  <p><strong>Exercise B (Alternate):</strong> Sing a note and manually oscillate between that pitch and a semitone above — slowly at first (2 per second), then speed it up. Don't let it become a trill; aim for a smooth undulation.</p>
                  <p><strong>At the piano:</strong> Play C4. Sustain for 8 beats. Start straight, release tension at beat 4. Note the difference.</p>
                  <p><strong>What it trains:</strong> Vibrato onset, tension release, oscillation control for both wide (Celine) and fast (Stevie) styles.</p>
                </div>
              </div>

              <div class="ex-card ex-tex">
                <div class="ex-tag" style="color:#f472b6;border-color:rgba(244,114,182,0.4);">TEX · Straight Tone</div>
                <div class="ex-title">Vibrato Suppression</div>
                <div class="ex-body">
                  <p>Straight tone (Elliott Smith, Sufjan Stevens) requires actively suppressing the natural vibrato impulse. This demands a different kind of control — precise breath pressure and slight intentional tension.</p>
                  <p><strong>At the piano:</strong> Play G3–G4. Sing a slow scale on "Ooh." Focus on holding each pitch like a laser beam — absolutely steady. If vibrato creeps in, apply a gentle, deliberate firmness (not a squeeze). Practice alternating: one note with vibrato, the next absolutely straight.</p>
                  <p><strong>What it trains:</strong> Vibrato control on/off, pitch precision, tonal color switching.</p>
                </div>
              </div>

              <div class="ex-card ex-tex">
                <div class="ex-tag" style="color:#f472b6;border-color:rgba(244,114,182,0.4);">TEX · Breath / Smoke</div>
                <div class="ex-title">Controlled Breathiness</div>
                <div class="ex-body">
                  <p>Deliberate breathiness (Norah Jones, early Adele) is excess air woven into the tone — the cords aren't fully approximating. A little goes a long way; too much causes vocal fatigue.</p>
                  <p><strong>Exercise:</strong> On a sustained "Hah," allow more air than you normally would — slightly more than your cleanest tone. Practice finding the exact blend: 70% tone, 30% breath. Then try 50/50. Then back to clean.</p>
                  <p><strong>At the piano:</strong> Play D3–D4. Sustain each note for 4 beats. First pass: clean. Second pass: intentionally smoky. Third pass: find your preferred blend.</p>
                  <p><strong>What it trains:</strong> Cord approximation control, breath/tone ratio awareness, tonal color palette.</p>
                </div>
              </div>

              <div class="ex-card ex-tex">
                <div class="ex-tag" style="color:#f472b6;border-color:rgba(244,114,182,0.4);">TEX · Compression</div>
                <div class="ex-title">Pressed Phonation Control</div>
                <div class="ex-body">
                  <p>Compression is a glottal pressing quality — the cords approximate firmly, creating a dense, intense tone (Cornell, Vedder). It must come from cord engagement, not from throat squeezing.</p>
                  <p><strong>Exercise:</strong> Cough very gently — you'll feel the glottal closure. Now try to reproduce that engagement while singing a sustained note — a firm "click" into the tone without a full cough. The tone should feel dense and present.</p>
                  <p><strong>At the piano:</strong> Play E3–B3. Sing "Gah" with intentional cord firmness. Compare to a relaxed "Hah" on the same pitch. The difference is compression.</p>
                  <p><strong>Important:</strong> Compression is taxing. Keep sessions short (5 minutes max) until it feels natural. Never squeeze from the throat muscles.</p>
                  <p><strong>What it trains:</strong> Glottal compression awareness, modal intensity, rock/soul grit without damage.</p>
                </div>
              </div>

              <div class="ex-card ex-tex">
                <div class="ex-tag" style="color:#f472b6;border-color:rgba(244,114,182,0.4);">TEX · Rasp / Growl</div>
                <div class="ex-title">Safe Distortion Approach</div>
                <div class="ex-body">
                  <p>Rasp (Rod Stewart) and growl (Chris Cornell, Tom Jones) involve controlled distortion of an otherwise clean tone — usually via epiglottal or aryepiglottic fold engagement overlying normal phonation. This should never feel raw or painful.</p>
                  <p><strong>Exercise (Rasp):</strong> Sustain a clean "Ah." Slowly add a slight growl to the onset — like the beginning of a snore. The underlying pitch should stay clean and supported; the distortion rides on top.</p>
                  <p><strong>Exercise (Growl):</strong> Sing "Rrroh" — the rolled R naturally engages the aryepiglottic folds. Try to sustain the growl character past the R into the vowel.</p>
                  <p><strong>Important:</strong> If it hurts — stop. Rasp and growl done correctly feel like a tickle or buzz, never a scrape. Always warm up thoroughly before attempting these.</p>
                  <p><strong>What it trains:</strong> Supraglottal distortion, rasp/growl color without cord damage.</p>
                </div>
              </div>

              <div class="ex-card ex-tex">
                <div class="ex-tag" style="color:#f472b6;border-color:rgba(244,114,182,0.4);">TEX · Covered</div>
                <div class="ex-title">Low Larynx — "Dark Warmth"</div>
                <div class="ex-body">
                  <p>Covered tone (Luther Vandross, Sinatra, Dr. Dre) uses a low laryngeal position to create warmth and depth. The sound is "veiled" or "rounded" rather than bright.</p>
                  <p><strong>Exercise:</strong> Yawn fully — notice your larynx drop and your throat open. Now sing "Oh" while trying to maintain that yawned space. The tone will darken noticeably.</p>
                  <p><strong>At the piano:</strong> Play C3–G4. Sustain "Oh" with the yawned larynx through the scale. Compare to your normal bright "Oh." The darker, rounder version is covered.</p>
                  <p><strong>What it trains:</strong> Low larynx positioning, dark resonance, warmth control.</p>
                </div>
              </div>
            </div>

            <!-- PHRASING EXERCISES -->
            <div id="exp-phrasing" class="ex-page" style="display:none;">
              <div style="font-size:11px;color:var(--text3);line-height:1.6;padding:8px 12px;background:rgba(45,212,191,0.05);border:1px solid rgba(45,212,191,0.2);border-radius:8px;margin-bottom:14px;">Phrasing is how you deliver the note — runs, portamento, speak-sing, microtonal inflection, and percussive interjections. These are largely stylistic skills, but they have physical foundations that can be practiced.</div>

              <div class="ex-card ex-phr">
                <div class="ex-tag" style="color:#2dd4bf;border-color:rgba(45,212,191,0.4);">PHR · Runs & Riffs</div>
                <div class="ex-title">Scale Runs — Slow to Fast</div>
                <div class="ex-body">
                  <p>Runs (Whitney, Mariah, Christina) are rapid scale passages — each note is a distinct pitch, not a blur. The most common error is speed: students try to go fast before they have the slow version clean.</p>
                  <p><strong>At the piano:</strong> Play a pentatonic scale: C–E–G–A–C (up), then C–A–G–E–C (down). Sing it slowly and clearly on "Ah" — every note distinct. Speed it up gradually. Once it's clean fast, try reversing direction mid-run: C–E–G–E–C–E–G. Then add embellishments.</p>
                  <p><strong>What to feel:</strong> Light, quick, independent movement. No jaw motion — the agility comes from tongue and soft palate, not facial muscles. Think of each note as a tiny, precise event.</p>
                  <p><strong>What it trains:</strong> Melismatic agility, pitch accuracy at speed, run construction.</p>
                </div>
              </div>

              <div class="ex-card ex-phr">
                <div class="ex-tag" style="color:#2dd4bf;border-color:rgba(45,212,191,0.4);">PHR · Portamento</div>
                <div class="ex-title">Controlled Slide Between Pitches</div>
                <div class="ex-body">
                  <p>Portamento (Elvis, Sinatra) is a deliberate, expressive glide between pitches — not a sloppy approach, but a controlled, stylistic slide that arrives cleanly on the target note.</p>
                  <p><strong>At the piano:</strong> Play C4, then G4. Sing C4 on "Ooh," then slide slowly and intentionally up to G4. The slide should start about 75% through the note's duration and arrive cleanly on G. Try it faster. Try it on a downward interval. Try 3rds, then octaves.</p>
                  <p><strong>What it trains:</strong> Intentional pitch glide, stylistic expression, laryngeal slide control.</p>
                </div>
              </div>

              <div class="ex-card ex-phr">
                <div class="ex-tag" style="color:#2dd4bf;border-color:rgba(45,212,191,0.4);">PHR · Speak-Sing</div>
                <div class="ex-title">Speech-to-Pitch Transition</div>
                <div class="ex-body">
                  <p>Speak-sing (Lou Reed, Rex Orange County, Tom Waits) sits at the boundary between speech and melody — often on or near a pitch but with speech inflection and rhythm. The key is that it feels natural, not performed.</p>
                  <p><strong>Exercise:</strong> Take a line from a song you know that uses speak-sing. First, simply speak it — fully committed, as if saying it to someone. Then gradually add the notated pitches while keeping the speech energy. Don't let it become "singing"; keep the conversational feel.</p>
                  <p><strong>At the piano:</strong> Play a single note (try F3 for a typical speak-sing register). Say a phrase ("I don't know what to do") on that pitch. It should feel slightly absurd — that's correct. Then apply it to an actual song lyric.</p>
                  <p><strong>What it trains:</strong> Speech-singing coordination, lyrical delivery, authenticity of phrasing.</p>
                </div>
              </div>

              <div class="ex-card ex-phr">
                <div class="ex-tag" style="color:#2dd4bf;border-color:rgba(45,212,191,0.4);">PHR · Microtonal</div>
                <div class="ex-title">Between-Note Inflection</div>
                <div class="ex-body">
                  <p>Microtonal inflection (Billie Eilish, Jeff Buckley) involves deliberate pitch bends, flat approaches, or quarter-tone inflections that fall between the standard pitches of the scale. Used sparingly, it conveys profound emotional nuance.</p>
                  <p><strong>Exercise:</strong> Sing a phrase from a song. On the emotionally weighted word, approach the note from a semitone or quarter-tone below — let the pitch "find" its way rather than arriving directly. Listen to Billie Eilish's "when the party's over" for the effect.</p>
                  <p><strong>At the piano:</strong> The piano can't play microtones, but use it to establish the target pitch. Then practice approaching it from below — starting flat and bending up into pitch.</p>
                  <p><strong>What it trains:</strong> Expressive pitch flexibility, emotional color, vocal personality.</p>
                </div>
              </div>

              <div class="ex-card ex-phr">
                <div class="ex-tag" style="color:#2dd4bf;border-color:rgba(45,212,191,0.4);">PHR · Grunt</div>
                <div class="ex-title">Percussive Vocal Interjections</div>
                <div class="ex-body">
                  <p>James Brown's "Ow!" and Michael Jackson's "Hee!" are not accidents — they're percussive interjections used rhythmically and expressively. They require strong breath support and precise timing.</p>
                  <p><strong>Exercise:</strong> With a metronome or drum beat, practice short interjections on specific beats: "Huh!" on beat 2, "Ow!" on the "and" of 4. They should feel explosive but supported — from the belly, not the throat.</p>
                  <p><strong>What to feel:</strong> A sharp burst of air and sound from the core. Your throat should stay open. Think of it like a mini-sneeze: sudden, supported, complete.</p>
                  <p><strong>What it trains:</strong> Rhythmic precision, percussive phonation, breath burst control.</p>
                </div>
              </div>

            </div><!-- end ex-pages -->
          </div>
        </div><!-- end vg-exercises -->

        <div style="border-top:1px solid var(--border);margin-bottom:24px;"></div>

        <!-- TECHNIQUE BUILDER -->
        <div id="prac-technique" style="margin-bottom:24px;">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:1px;color:var(--text2);margin-bottom:4px;">🎤 Technique Builder</div>
          <div style="font-size:12px;color:var(--text3);margin-bottom:14px;line-height:1.6;">Select a technique to see targeted exercises for developing it.</div>
          <div id="vg-technique-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;"></div>
          <div id="vg-tech-detail" style="display:none;">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
              <div id="vg-tech-title" style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:1px;color:#a78bfa;"></div>
              <button onclick="document.getElementById('vg-tech-detail').style.display='none';document.getElementById('vg-technique-grid').querySelectorAll('button[data-tech-id]').forEach(function(b){b.style.background='var(--surface)';b.style.borderColor=''})" style="background:none;border:none;color:var(--text3);font-size:18px;cursor:pointer;">✕</button>
            </div>
            <div id="vg-tech-exemplars" style="font-size:11px;color:var(--text3);font-style:italic;margin-bottom:14px;line-height:1.5;"></div>
            <div id="vg-tech-exercises" style="display:flex;flex-direction:column;gap:10px;"></div>
          </div>
        </div>

        <div style="border-top:1px solid var(--border);margin-bottom:24px;"></div>

        <!-- SUGGESTED READING -->
        <div id="vg-reading" style="margin-bottom:24px;">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:1px;color:var(--text2);margin-bottom:4px;">📚 Suggested Reading</div>
          <div style="font-size:12px;color:var(--text3);margin-bottom:14px;line-height:1.6;">Resources for singers who want to go deeper on technique. Arranged from most accessible to most technical.</div>

          <div class="ex-card">
            <div class="ex-title">Singing and the Actor — Gillyanne Kayes</div>
            <div class="ex-body">
              <p>The most practically useful book on this list for contemporary and musical theatre singers. Bridges classical and CCM technique with clear explanations of registration, belting, and resonance. Strong on why the body does what it does and how to access different qualities intentionally. Written for working performers, not academics.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">The Diagnosis and Correction of Vocal Faults — James C. McKinney</div>
            <div class="ex-body">
              <p>The most systematic fault-identification resource in print. Each chapter isolates a specific fault (breathiness, nasality, tension, register breaks, etc.), describes its acoustic signature, explains its cause, and provides corrective exercises. The Fault Correction section above draws on this framework. Originally written for teachers, but readable by any motivated singer who wants to diagnose their own voice methodically.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Complete Vocal Technique (CVT) — Cathrine Sadolin</div>
            <div class="ex-body">
              <p>A four-mode framework — Neutral, Curbing, Overdrive, Edge — that maps every sound the voice can make onto distinct physiological configurations. Particularly strong on distortion techniques (what they are, how to produce them safely, and how to distinguish them perceptually). Available as a book and as a comprehensive online resource at the Complete Vocal Institute website. One of the few systems that addresses heavy metal and extreme vocal techniques in a scientifically grounded way.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">Estill Voice Training — Jo Estill (Estill Voice International)</div>
            <div class="ex-body">
              <p>Isolates 13 individual vocal "figures" — discrete laryngeal and vocal tract configurations — for independent training. The pedagogical premise is that you can learn to control each structure (e.g., thyroid tilt, aryepiglottic sphincter, soft palate) separately, then combine them for any desired quality. The most analytically precise system on this list. Formal training through an EVI-certified teacher is the intended delivery method, but the conceptual framework is available through published materials and online resources.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">The Structure of Singing — Richard Miller</div>
            <div class="ex-body">
              <p>The authoritative technical treatment of the classical Western tradition — breath mechanics, onset, vowel formation, resonance, registration, and range. Dense and thorough. Less directly applicable to rock, pop, or R&amp;B than the other resources on this list, but provides the deepest anatomical and acoustic grounding of any widely available singing text. Essential reading if you want to understand the physiology behind the exercises in this app.</p>
            </div>
          </div>

          <div class="ex-card">
            <div class="ex-title">The Voice Book — Kate DeVore &amp; Starr Seidler</div>
            <div class="ex-body">
              <p>The most accessible entry point on this list — written for actors and speakers as much as singers. Covers voice anatomy, care and hygiene, breath, resonance, and basic technique without assuming prior knowledge. A good first book for singers who find the others too technical. Also the best resource on this list for voice health: what to do when your voice is tired or strained, how to recover, and how to avoid damage.</p>
            </div>
          </div>
        </div>

          </div><!-- end vg-toc-content -->
        </div><!-- end vg-toc-layout -->
      </div><!-- end vgp-practice -->

      <!-- PAGE: VOICE PROFILES -->
      <div id="vgp-profiles" class="vg-page" style="display:none;">
        <!-- Search + filter row -->
        <div style="display:flex;gap:8px;margin-bottom:8px;">
          <input id="vgp-artist-search" type="text" placeholder="Search famous voice profiles…" oninput="renderVgpArtistCatalog()"
            style="flex:1;background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:7px 10px;font-size:13px;color:var(--text);" />
          <select id="vgp-artist-filter-vox" onchange="renderVgpArtistCatalog()"
            style="background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:6px 8px;font-size:12px;color:var(--text);min-width:70px;">
            <option value="">All ↕</option>
            <option value="↑">↑ High</option>
            <option value="→">→ Mid</option>
            <option value="↓">↓ Low</option>
          </select>
          <select id="vgp-artist-sort" onchange="renderVgpArtistCatalog()"
            style="background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:6px 8px;font-size:12px;color:var(--text);min-width:60px;">
            <option value="az">A→Z</option>
            <option value="za">Z→A</option>
          </select>
        </div>
        <!-- Owner review tracker (EDIT_MODE only): progress + 3-state filter row -->
        <div id="vgp-review-progress" style="display:none;font-size:11px;color:var(--text2);margin-bottom:6px;padding:7px 10px;background:var(--surface);border:1px solid var(--border);border-radius:8px;line-height:1.5;"></div>
        <div id="vgp-review-filter-row" style="display:none;gap:6px;flex-wrap:wrap;margin-bottom:8px;">
          <button class="vgp-rev-btn active" onclick="setVgpReviewFilter('',this)" style="padding:4px 10px;border-radius:12px;border:1px solid var(--border);background:var(--surface2);color:var(--text2);font-size:11px;cursor:pointer;">All</button>
          <button class="vgp-rev-btn" onclick="setVgpReviewFilter('untouched',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(255,255,255,0.18);background:rgba(255,255,255,0.04);color:var(--text2);font-size:11px;cursor:pointer;">⬜ Untouched</button>
          <button class="vgp-rev-btn" onclick="setVgpReviewFilter('deferred',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(245,158,11,0.4);background:rgba(245,158,11,0.08);color:#f59e0b;font-size:11px;cursor:pointer;">🔶 Deferred</button>
          <button class="vgp-rev-btn" onclick="setVgpReviewFilter('reviewed',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(34,197,94,0.4);background:rgba(34,197,94,0.08);color:#22c55e;font-size:11px;cursor:pointer;">✅ Reviewed</button>
          <button class="vgp-rev-btn" onclick="setVgpReviewFilter('open',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(124,58,237,0.4);background:rgba(124,58,237,0.08);color:#a78bfa;font-size:11px;cursor:pointer;" title="Untouched + Deferred (everything still to do)">🎯 Open</button>
        </div>
        <div id="vgp-source-filter-row" style="display:none;gap:6px;flex-wrap:wrap;margin-bottom:8px;">
          <button class="vgp-src-btn active" onclick="setVgpSourceFilter('',this)" style="padding:4px 10px;border-radius:12px;border:1px solid var(--border);background:var(--surface2);color:var(--text2);font-size:11px;cursor:pointer;">All</button>
          <button class="vgp-src-btn" onclick="setVgpSourceFilter('empty',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(239,68,68,0.4);background:rgba(239,68,68,0.08);color:#ef4444;font-size:11px;cursor:pointer;">⚠️ No FVP data</button>
          <button class="vgp-src-btn" onclick="setVgpSourceFilter('legacy',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(255,255,255,0.18);background:rgba(255,255,255,0.04);color:var(--text2);font-size:11px;cursor:pointer;">📜 Legacy</button>
          <button class="vgp-src-btn" onclick="setVgpSourceFilter('ai',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(124,58,237,0.4);background:rgba(124,58,237,0.08);color:#a78bfa;font-size:11px;cursor:pointer;">🤖 Gemini draft</button>
          <button class="vgp-src-btn" onclick="setVgpSourceFilter('claude',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(56,189,248,0.4);background:rgba(56,189,248,0.08);color:#38bdf8;font-size:11px;cursor:pointer;">🤖 Claude draft</button>
          <button class="vgp-src-btn" onclick="setVgpSourceFilter('human',this)" style="padding:4px 10px;border-radius:12px;border:1px solid rgba(34,197,94,0.4);background:rgba(34,197,94,0.08);color:#22c55e;font-size:11px;cursor:pointer;">✓ Reviewed</button>
        </div>
        <!-- Voice Filters superdrawer (collapses Foundation + Style sub-toggles) -->
        <div style="margin-bottom:10px;">
          <button class="btn btn-sm" id="vgp-voice-filter-btn" onclick="toggleVgpVoiceFilterDrawer()"
            style="width:100%;justify-content:center;font-size:11px;border-radius:20px;background:rgba(34,211,238,0.1);border:1px solid rgba(34,211,238,0.35);color:#67e8f9;padding:5px 4px;">＋ Voice Filters</button>
          <div id="vgp-voice-filter-drawer" style="display:none;margin-top:8px;">
            <div style="font-size:11px;color:var(--text3);margin-bottom:8px;line-height:1.6;">Select "or" vs. "and" logic when applying multiple voice filters. Toggle drawers to view filters, tap to apply. Active filters survive drawer close and persist until cleared.</div>
            <div style="display:flex;align-items:center;gap:6px;margin-bottom:8px;flex-wrap:wrap;">
              <span style="font-size:11px;font-weight:600;color:var(--text3);white-space:nowrap;">Next Tag Logic:</span>
              <div id="vgp-filter-mode-btn" onclick="toggleVgpFilterMode()" style="display:flex;align-items:center;gap:4px;cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;"></div>
            </div>
            <div class="ios-toggle-row" style="margin-bottom:8px;">
              <div style="display:flex;align-items:center;gap:0;">
                <div class="ios-toggle-label" role="button" onclick="toggleVgpShowFoundation()" style="cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;">
                  <div id="vgp-toggle-foundation" class="ios-toggle-track"><div class="ios-toggle-knob"></div></div>
                  <span>🎙️ Voice Foundation<span id="vgp-foundation-badge" style="color:#38bdf8;font-weight:700;"></span></span>
                </div>
                <span onclick="event.stopPropagation(); showFilterHelp('vox');" style="display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;font-size:9px;font-weight:700;cursor:pointer;margin-left:4px;flex-shrink:0;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;background:rgba(255,255,255,0.1);color:var(--text3);border:1px solid rgba(255,255,255,0.15);">?</span>
              </div>
              <div style="display:flex;align-items:center;gap:0;">
                <div class="ios-toggle-label" role="button" onclick="toggleVgpShowStyle()" style="cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;">
                  <div id="vgp-toggle-style" class="ios-toggle-track"><div class="ios-toggle-knob"></div></div>
                  <span>🎨 Voice Style<span id="vgp-style-badge" style="color:#22d3ee;font-weight:700;"></span></span>
                </div>
                <span onclick="event.stopPropagation(); showFilterHelp('voxstyle');" style="display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;font-size:9px;font-weight:700;cursor:pointer;margin-left:4px;flex-shrink:0;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;background:rgba(255,255,255,0.1);color:var(--text3);border:1px solid rgba(255,255,255,0.15);">?</span>
              </div>
            </div>
            <div id="vgp-drawer-foundation" style="display:none;margin-bottom:6px;">
              <div id="vgp-tag-row-foundation" style="display:flex;flex-wrap:wrap;gap:5px;"></div>
            </div>
            <div id="vgp-drawer-style" style="display:none;margin-bottom:6px;">
              <div id="vgp-tag-row-style" style="display:flex;flex-wrap:wrap;gap:5px;"></div>
            </div>
            <div style="text-align:right;margin-top:8px;">
              <span onclick="toggleVgpVoiceFilterDrawer()" style="font-size:12px;color:var(--text3);cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;">▲ Close filters</span>
            </div>
          </div>
        </div>
        <!-- Artist list -->
        <div id="vgp-artist-list" style="display:flex;flex-direction:column;gap:6px;"></div>
        <div id="vgp-artist-empty" style="display:none;text-align:center;padding:32px;color:var(--text3);font-size:13px;">No artists match your filter.</div>
        <!-- A–Z scrub rail for the 800+ profiles (2026-06-09) — reuses the
             Songbook .sb-rail CSS + _sbRailLetterAtY; auto-hides when the
             profiles page (or the whole tab) is display:none. -->
        <div id="vgp-rail" class="sb-rail" style="display:none;"></div>
      </div>

      <!-- PAGE: MY VOICE -->
      <div id="vgp-memos" class="vg-page" style="display:none;padding-top:8px;">

        <!-- Dashboard hero — heads the whole My Voice tab (2026-06-09). -->
        <div class="mv-hero">
          <div class="mv-hero-title">YOUR VOICE</div>
          <div class="mv-hero-sub">Record it, track it, train it — your instrument, over time.</div>
        </div>

        <!-- My Voice sub-toggle -->
        <div style="display:flex;gap:6px;margin-bottom:16px;">
          <button id="myvoice-btn-profile" class="tab-sub-btn" style="flex:1;" onclick="setMyVoiceView('profile',this)">🎙️ My Voice Profile</button>
          <button id="myvoice-btn-notes" class="tab-sub-btn active" style="flex:1;" onclick="setMyVoiceView('notes',this)">🗂 Voice Notes</button>
        </div>

        <!-- Sub-page: My Voice Profile — the future home of the paid voice
             dashboard. For now, a locked teaser (desire-builder). -->
        <div id="myvoice-profile-page" style="display:none;">
          <div class="mv-locked">
            <div class="mv-locked-shine" aria-hidden="true"></div>
            <div class="mv-locked-badge">🔒 PAID</div>
            <div class="mv-locked-icon">🎙️</div>
            <div class="mv-locked-title">AI Voice Analysis</div>
            <div class="mv-locked-desc">Your range, register &amp; signature techniques — analyzed from your recordings and tracked as you grow. A Madden-style vocal stats card, gap analysis, and song-fit scoring.</div>
            <div class="mv-locked-foot">✨ Coming with a BAR subscription</div>
          </div>
        </div>

        <!-- Sub-page: Voice Notes (memo vault) -->
        <div id="myvoice-notes-page" style="display:none;">
          <!-- Inline voice-memo recorder (2026-06-09) — replaced the global mic
               FAB. #mv-rec-btn calls audioFabRecord() (the existing start/stop
               toggle); _setRecUI(active) (audio_fab.js) drives the .is-recording
               state, the live timer, and the STOP affordance. -->
          <button id="mv-rec-btn" class="mv-rec-widget" onclick="audioFabRecord()">
            <span class="mv-rec-glyph"><span class="mv-rec-mic">🎤</span><span class="mv-rec-dot"></span></span>
            <span class="mv-rec-text">
              <span class="mv-rec-title">Record a vocal memo</span>
              <span class="mv-rec-sub">Capture your range, tone, or a song idea</span>
            </span>
            <span class="mv-rec-timer" id="mv-rec-timer">0:00</span>
            <span class="mv-rec-stop">◼ STOP</span>
          </button>
          <div style="display:flex;justify-content:flex-end;margin-bottom:12px;">
            <button class="btn btn-secondary btn-sm" onclick="renderMemoVault()" style="font-size:12px;">↻ Refresh</button>
          </div>
          <div id="memo-vault-list">
            <div style="text-align:center;color:var(--text3);padding:40px 0;font-size:13px;">
              Tap ↻ Refresh or switch to this tab to load your memos.
            </div>
          </div>

          <!-- Bridge into the Library hub — "Continue Learning" (2026-06-09). -->
          <button class="vg-lib-row mv-bridge" onclick="showVgPage('library')">
            <span class="vg-lib-ico">📚</span>
            <span class="vg-lib-txt"><span class="vg-lib-title">Keep learning</span><span class="vg-lib-sub">Techniques, Famous Voices &amp; more in the Library</span></span>
            <span class="vg-lib-chev">›</span>
          </button>
        </div>

      </div><!-- end vgp-memos -->

      <!-- TOC floating toggle — shown on article sub-pages -->
      <button id="vg-toc-btn" class="vg-toc-toggle" onclick="toggleVgToc()">TOC</button>

    </div><!-- end vg-window -->
  </div><!-- end tab-voxgym -->

  <!-- TONIGHT TAB -->
  <div id="tab-night" class="panel" style="margin-top:-16px;">

    <!-- STICKY SUB-NAV — Apple-fy Phase 2 (2026-06-06): 5 buttons → 3 in a
         single row. Log / Find / Capture. Find is a container hosting AI Search /
         Randomizer / Requests as inner pills (default = AI Search). External
         callers using the old view keys (smartsearch, randomiser, requests)
         still work — setNightView routes them to the right inner pill + lights
         the right top-level button. The Requests badge moved onto the Find
         button so users see new requests even when not in Find. -->
    <div id="night-subnav" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:8px 16px 10px;border-bottom:1px solid var(--border);">
      <div style="display:flex;gap:5px;">
        <button id="night-subnav-log" class="tab-sub-btn active" onclick="setNightView('setlist',this)"
          style="flex:1;position:relative;">🎤 Tonight<span id="upnext-tonight-badge" style="display:none;position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;border-radius:8px;background:var(--accent3);color:#fff;font-size:10px;font-weight:700;line-height:16px;text-align:center;padding:0 3px;box-sizing:border-box;pointer-events:none;"></span></button>
        <button id="night-subnav-find" class="tab-sub-btn" onclick="setNightView('smartsearch',this)"
          style="flex:1;">💡 Ideas</button>
        <!-- requests-subnav-badge removed (2026-06-07, Step 2) — requests moved to the
             Tonight board; the incoming-request count rides the Sing bottom-tab
             badge (perform-badge) + the inline 📥 Requests section instead. -->
        <button id="night-subnav-capture" class="tab-sub-btn" onclick="setNightView('studio',this)"
          style="flex:1;">🎬 Capture</button>
      </div>
      <!-- Find inner-pill row RETIRED (2026-06-07, Ideas→Find Step 3): AI Search +
           Randomizer MERGED into ONE 💡 Ideas surface (search box, scope toggle;
           empty state = the dice). So Ideas is a single surface, no inner pills.
           Requests left earlier (Step 2 → Tonight board). -->
    </div>

    <!-- First-session orientation card. Populated by _renderOrientCardInto
         on app load. Empty until then; auto-hides via display:none when the
         user has dismissed it (kk_orient_sing_dismissed). Restore via
         Settings → Start → "💡 Show orientation tips". Sits above all
         sub-pages so it's visible regardless of which sub-view is active. -->
    <div id="orient-sing-container"></div>

    <!-- #night-randomiser RETIRED (2026-06-07, Ideas→Find Step 3) — the Randomizer
         MERGED into the unified Find surface: its empty state IS the dice (see
         _renderFindDice in smart-search.js, which hosts rand-mode-1/3 +
         rand-spin-btn + rand-result-area). Kept as an empty placeholder div so
         renderNightView's display reference stays null-safe; it's never shown
         (setNightView redirects 'randomiser' → 'smartsearch'). The dice control
         IDs must NOT be duplicated here. -->
    <div id="night-randomiser" style="display:none;"></div>

    <div id="night-setlist" style="padding-top:14px;">

      <!-- The Tonight's Venue card was removed 2026-06-06 (Sing tab Apple-fy
           Phase 1). The header venue chip (Row 2) + the canonical Set Venue
           sheet are the single source of truth now. Folded into the sheet:
           the perf-type toggle (only shown when ambiguous), the hub link,
           and auto-add-to-My-Venues on commit. Eliminated 4 surfaces of
           competing UI for one task. -->

      <!-- QUICK-PICK ZONE (2026-06-08 Tonight reorg) — the DECISIVE
           tools sit at the top: "I know roughly what I want, let me grab it."
           ① Browse & Pick hero → the combined A–Z + filter sheet (openSongPicker),
           per-row ⚡ Log / ＋ Backstage. ② ⚡ Single (type one) + 🎲 Random just
           below. The suggestion grid ("need a nudge?") moved DOWN to #tonight-extra,
           below the staging area — it's the help-me-decide afterthought now. -->
      <button onclick="openSongPicker('tonight')" class="tb-browse-hero" title="Browse your songbook and pick without typing">
        <span class="tb-browse-icon">🔤</span>
        <span class="tb-browse-text">
          <span class="tb-browse-title">Pick a song</span>
          <span class="tb-browse-sub">Browse A–Z or filter by vibe — log it or add to Backstage</span>
        </span>
        <span class="tb-browse-cv">›</span>
      </button>

      <!-- ⚡ Single card RETIRED 2026-06-08. Its jobs folded into the
           🔤 Pick a song sheet: search-and-log (per-row ⚡ Log), manual-entry log
           (_pickerLogManual), 🎲 Random (picker "🎲 Surprise me"), and the Duet
           checkbox (picker "🎭 Duet" toggle). The lens grid below still gives
           inline one-tap logging for usual songs. The quicklog* JS functions in
           setlist.js are now dead (no caller) — left in place; safe to delete in
           a follow-up cleanup. -->

      <!-- Reward banner host (renderTonightBoard → #tonight-board). The Backstage
           queue used to render here too; it's now the un-tucked area below. -->
      <div id="tonight-board"></div>

      <!-- SECONDARY INPUTS (2026-06-09 "calculator IA" reorg) — 📥 Requests + the
           "browse my songs by vibe" lens grid. Moved ABOVE Backstage so ALL the
           decide-what-to-sing surfaces sit up top (inputs), leaving the bottom
           purely for the output (Tonight's Ledger). Filled by renderTonightBoard
           → #tonight-suggest. -->
      <div id="tonight-suggest"></div>

      <!-- 🎭 BACKSTAGE — the ONE staging surface (2026-06-08). The
           separate tucked "Set tools" was merged in: this is the queue AND its
           tools. Songs = currentSetlist; rich rows (reorder ≡ / duet ♫ / ⚡ log /
           remove ✕) rendered by renderSetlist into #setlist-songs. -->
      <div id="backstage-area" class="backstage-area" style="margin-top:18px;margin-bottom:16px;">
        <!-- Header: title + count · New/Saved · Clear -->
        <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:8px;">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:18px;letter-spacing:1px;color:var(--text);">🎭 Backstage<span id="backstage-count" style="color:var(--text3);font-size:14px;"></span></div>
          <div style="display:flex;align-items:center;gap:8px;">
            <div style="display:inline-flex;background:var(--surface2);border:1px solid var(--border);border-radius:20px;padding:2px;">
              <button id="setlist-mode-new-btn" onclick="setSetlistMode('new')"
                style="border:none;border-radius:18px;padding:4px 14px;font-size:12px;cursor:pointer;transition:background .15s,color .15s;background:var(--accent);color:#fff;font-weight:600;">New</button>
              <button id="setlist-mode-saved-btn" onclick="setSetlistMode('saved')"
                style="border:none;border-radius:18px;padding:4px 14px;font-size:12px;cursor:pointer;transition:background .15s,color .15s;background:transparent;color:var(--text2);font-weight:400;">Saved</button>
            </div>
            <button onclick="clearSetlist()" style="background:none;border:none;font-size:12px;color:var(--text3);cursor:pointer;padding:4px 0;touch-action:manipulation;">Clear</button>
          </div>
        </div>

        <!-- Saved setlist picker — shown when Saved mode is active -->
        <div id="setlist-saved-picker" style="display:none;margin-bottom:10px;"></div>

        <div id="setlist-form-content">
          <!-- Setlist songs (rich rows) -->
          <div id="setlist-songs"></div>

          <!-- Inline label form — revealed only when Save is tapped -->
          <div id="setlist-save-label-form" style="display:none;margin-top:8px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px;">
            <div style="font-size:12px;color:var(--text2);margin-bottom:6px;">Name this setlist:</div>
            <div style="display:flex;gap:6px;">
              <input class="form-input" id="setlist-name" placeholder="e.g. Crow Bar Thu" style="flex:1;padding:5px 10px;font-size:13px;"
                onkeydown="if(event.key==='Enter')confirmSaveSetlist()">
              <button class="btn btn-primary btn-sm" onclick="confirmSaveSetlist()">Save</button>
              <button class="btn btn-secondary btn-sm" onclick="document.getElementById('setlist-save-label-form').style.display='none'">✕</button>
            </div>
          </div>

          <!-- Backstage tool bar — the merged Set tools, always visible -->
          <div class="backstage-tools">
            <button class="btn btn-primary btn-sm" onclick="openSongPicker('setlist')" title="Browse / search to add songs">＋ Add</button>
            <button class="btn btn-sm backstage-tool-ai" onclick="openSetlistAiFill()" title="AI fills empty slots in your setlist">🎬 AI Fill</button>
            <button class="btn btn-sm backstage-tool-save" onclick="showSaveSetlistForm()" title="Save this as a reusable setlist">💾 Save</button>
            <button class="btn btn-sm backstage-tool-logall" onclick="logAllSetlist()" title="Log every song in Backstage as performed">▶ Log All</button>
          </div>
        </div>
      </div>

      <!-- 📒 TONIGHT'S LEDGER (2026-06-09) — the OUTPUT readout: the running tally of
           what you've performed+logged tonight ("How'd it go?"). The calculator's
           result display — nothing to-sing sits below it now (the browse/requests
           inputs moved up to #tonight-suggest above Backstage). Filled by
           renderTonightBoard() → #tonight-extra. -->
      <div id="tonight-extra"></div>
    </div>

    <div id="night-requests" style="display:none;padding-top:14px;">
      <div id="requests-container"></div>
    </div>

    <div id="night-smartsearch" style="display:none;padding-top:14px;">
      <div id="smartsearch-container"></div>
    </div>

    <div id="night-studio" style="display:none;padding-top:14px;">
      <div id="studio-container"></div>
    </div>
  </div>

  <!-- PEOPLE TAB -->
  <div id="tab-people" class="panel" style="margin-top:-16px;">
    <!-- Sticky subnav. Collapsed from 3 buttons to 2. The
         former 'Friends' and 'Messages' tabs unified into one People list
         (renderFriendsTab now renders both accepted friends + pending
         requests + unread-message badges in a single sorted list).
         The Messages sub-page DOM is retained behind the scenes for the
         thread view (push-notif click, openMsgThreadByFId paths still
         use it). Just no direct sub-nav button anymore.
         The unread badge that was on the Messages button now lives on the
         People button itself (total unread across all friends). -->
    <div id="people-subnav" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:8px 16px 10px;border-bottom:1px solid var(--border);">
      <div style="display:flex;gap:6px;">
        <button class="tab-sub-btn active" id="people-sub-friends"   onclick="setPeopleView('friends',this)"   style="flex:1;position:relative;">👥 Friends<span id="msg-subnav-badge" style="display:none;position:absolute;top:1px;right:4px;min-width:14px;height:14px;border-radius:7px;background:var(--accent);color:#fff;font-size:9px;font-weight:700;line-height:14px;text-align:center;padding:0 3px;box-sizing:border-box;pointer-events:none;"></span></button>
        <button class="tab-sub-btn"        id="people-sub-addfriend" onclick="setPeopleView('addfriend',this)" style="flex:1;">➕ Add New</button>
      </div>
    </div>

    <!-- First-session orientation card. Populated by _renderOrientCardInto
         on app load. Empty until then; auto-hides via display:none when the
         user has dismissed it (kk_orient_people_dismissed). Restore via
         Settings → Start → "💡 Show orientation tips". Sits above all
         sub-pages so it's visible regardless of which sub-view is active. -->
    <div id="orient-people-container"></div>

    <!-- Add New sub-page -->
    <div id="people-addfriend-page" style="display:none;padding-top:14px;">
      <!-- Mode toggle -->
      <div style="display:flex;gap:6px;margin-bottom:16px;">
        <button id="addnew-btn-barfriends" class="tab-sub-btn active" onclick="setAddNewMode('barfriends',this)" style="flex:1;">👥 BAR Friends</button>
        <button id="addnew-btn-napkin"     class="tab-sub-btn"        onclick="setAddNewMode('napkin',this)"     style="flex:1;">📝 Napkin Notes</button>
      </div>

      <!-- ── BAR Friends section ──────────────────────────────────────── -->
      <!-- 2026-06-06 — friend-connect simplified to ONE model: search a BAR name →
           instant request → the receiver approves/denies. The old A/B/C/D matrix
           (12-hour delay, Connect Code, QR Handshake) was retired; BAR's
           anonymity (messaging off + all share-flags off by default + profile
           fields already authed-readable) makes "approve-on-receive" enough. -->
      <div id="addnew-barfriends-section">

        <!-- Add a Friend (search → instant request → they approve) -->
        <div style="font-size:11px;color:var(--text3);text-transform:uppercase;letter-spacing:1px;font-weight:700;margin-bottom:10px;">Add a Friend</div>
        <div style="margin-bottom:16px;">
          <div style="font-size:11px;color:var(--text3);margin-bottom:8px;line-height:1.5;">Search anyone by BAR name and send a request — they approve it on their end. No public directory; results only appear when you search.</div>
          <div style="display:flex;gap:8px;">
            <input class="form-input" id="friend-search-input" type="text" placeholder="Search by BAR name…" autocomplete="off" style="flex:1;height:38px;"
              oninput="onFriendSearchInput(this.value)" onkeydown="if(event.key==='Enter')searchFriendByName()">
            <button class="btn btn-primary" style="height:38px;padding:0 14px;font-size:13px;white-space:nowrap;" onclick="searchFriendByName()">Search</button>
          </div>
          <div id="friend-search-results" style="margin-top:6px;"></div>
          <div id="friend-req-msg" style="font-size:12px;margin-top:6px;display:none;"></div>
        </div>

        <!-- Your BAR name hint — populated by _fillMyBarNameHint() -->
        <div style="font-size:11px;color:var(--text3);background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:20px;line-height:1.5;">
          Your BAR name is <strong id="addnew-my-barname" style="color:var(--text);">…</strong> — tell people to search it to add you.
        </div>

        <!-- Invite to BAR (bring a non-user onto BAR) -->
        <div style="font-size:11px;color:var(--text3);text-transform:uppercase;letter-spacing:1px;font-weight:700;margin-bottom:10px;">Invite to BAR</div>
        <div style="margin-bottom:18px;padding:12px;background:var(--surface2);border-radius:var(--radius-sm);">
          <div style="font-size:11px;color:var(--text3);margin-bottom:10px;line-height:1.5;">Not on BAR yet? Share your invite QR or link. They sign up, and a friend request from you is waiting — they just tap accept.</div>
          <button class="btn btn-secondary" style="width:100%;height:36px;font-size:12px;" onclick="showMyQR()">📲 Show My Invite QR / Link</button>
        </div>
      </div>

      <!-- ── Napkin Notes section ─────────────────────────────────────── -->
      <div id="addnew-napkin-section" style="display:none;">
        <div style="font-size:11px;color:var(--text3);margin-bottom:12px;line-height:1.5;">Private, local-only notes about people you've met. Not connected to BAR — just a scratch pad so you don't forget a name.</div>
        <div class="search-bar" style="margin-bottom:10px;">
          <input class="search-input" type="text" placeholder="Search notes…" oninput="renderPeople()" id="people-search">
          <button class="btn btn-primary btn-sm" onclick="openAddPerson()">＋ Add</button>
        </div>
        <div id="people-list"></div>
      </div>
    </div>

    <!-- Friends sub-page -->
    <div id="people-friends-page" style="padding-top:14px;">
      <div id="friends-list"></div>
    </div>

    <!-- Messages sub-page. The Messages sub-nav button was retired in the
         unified-People-list overhaul, but this view is still the home of
         the chat thread (#msg-thread). It's shown by _openMsgThreadUI when
         a chat is opened (via the IM button on a friend card, push
         notification, or profile modal). The #msg-thread element gets
         body.bar-msg-thread-open + position:fixed to cover the viewport
         when actually displaying a conversation — canonical iMessage/
         WhatsApp pattern. Chat is INTENTIONALLY a separate surface from
         the friend-detail "share viewer" page, because 2-way comm is
         qualitatively different from 1-way share viewing. -->
    <div id="people-messages-page" style="display:none;padding-top:14px;">
      <!-- Convo list — still present but effectively unreachable in normal
           nav. Kept so renderConvoList() call sites have a target. -->
      <div id="msg-convo-list"></div>
      <!-- Thread view — full-screen chat overlay when shown. Toggled by
           openMsgThread / closeMsgThread via body.bar-msg-thread-open. -->
      <div id="msg-thread" style="display:none;">
        <div style="display:flex;align-items:center;gap:10px;padding:0 0 8px;border-bottom:1px solid var(--border);margin-bottom:8px;">
          <button onclick="closeMsgThread()" style="background:none;border:none;color:var(--accent2);font-size:14px;cursor:pointer;padding:4px 0;font-family:'DM Sans',sans-serif;">← Back</button>
          <div id="msg-thread-name" style="font-size:14px;font-weight:700;color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></div>
        </div>
        <div id="msg-thread-body" style="min-height:200px;max-height:55vh;overflow-y:auto;display:flex;flex-direction:column;gap:8px;margin-bottom:12px;padding-right:2px;"></div>
        <!-- "Friend is typing…" indicator — shown when the other party
             is broadcasting typing events on our shared channel. -->
        <div id="msg-typing-indicator" style="display:none;font-size:11px;color:var(--text3);font-style:italic;padding:0 4px 6px;">
          <span class="typing-dots"><span></span><span></span><span></span></span> is typing…
        </div>
        <!-- Edit-mode banner — shown when the user picked "Edit" from a
             message's action menu. Send button now updates instead of inserts. -->
        <div id="msg-edit-banner" style="display:none;align-items:center;gap:8px;padding:6px 10px;margin-bottom:6px;background:rgba(245,158,11,0.12);border:1px solid rgba(245,158,11,0.35);border-radius:8px;font-size:12px;color:#f59e0b;">
          <span style="flex:1;">✏️ Editing message</span>
          <button onclick="cancelEditMessage()" style="background:none;border:none;color:#f59e0b;font-size:13px;font-weight:600;cursor:pointer;padding:2px 6px;font-family:'DM Sans',sans-serif;">Cancel</button>
        </div>
        <div style="display:flex;gap:8px;">
          <textarea class="form-input" id="msg-input" placeholder="Message…" maxlength="1000" rows="1"
            name="bar-message-body" autocomplete="off" autocapitalize="sentences" autocorrect="on"
            spellcheck="true" enterkeyhint="send"
            data-form-type="other" data-lpignore="true" data-1p-ignore data-bwignore="true"
            style="flex:1;min-height:38px;max-height:120px;resize:none;line-height:1.3;padding-top:8px;padding-bottom:8px;overflow-y:auto;"
            onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMessage()}"></textarea>
          <button class="btn btn-primary" style="height:38px;padding:0 14px;font-size:13px;white-space:nowrap;" onclick="sendMessage()">Send</button>
        </div>
        <!-- "↓ N new" floater shown when the user is scrolled up in
             history and new messages arrive. Tap to jump to latest. -->
        <div id="msg-new-floater" onclick="scrollToThreadBottom()" style="display:none;"></div>
      </div>
    </div>

      <!-- Long-press / right-click action menu for individual messages.
           Positioned outside #msg-thread so the position:fixed overlay
           styling doesn't apply (this is its own fixed-position popup). -->
      <div id="msg-action-menu" style="display:none;">
        <!-- Quick-react bar — 6 fixed emojis above the menu items. -->
        <div class="msg-react-bar">
          <button onclick="_msgActionReact('👍')">👍</button>
          <button onclick="_msgActionReact('❤️')">❤️</button>
          <button onclick="_msgActionReact('😂')">😂</button>
          <button onclick="_msgActionReact('😮')">😮</button>
          <button onclick="_msgActionReact('😢')">😢</button>
          <button onclick="_msgActionReact('🙏')">🙏</button>
        </div>
        <button onclick="_msgActionCopy()">📋 Copy</button>
        <button data-own-only onclick="_msgActionEdit()">✏️ Edit</button>
        <button data-own-only onclick="_msgActionDelete()">🗑 Delete</button>
      </div>

    <!-- Friend Detail sub-page (drilled from Friends list) -->
    <!-- Container ID kept as people-friendsongs-page for backwards-compat with setPeopleView('friendsongs',...) -->
    <div id="people-friendsongs-page" style="display:none;">
      <!-- Friend-detail drawer — sticks where the People sub-nav normally is,
           but rendered to look like a slim drawer with the friend's name + ✕
           close on the left followed by available tab buttons. The People
           sub-nav itself is hidden via body.bar-in-friend-detail CSS rule
           while this drawer is showing, so the screen has the same number
           of sticky bars in either context.
           Friend name still rendered into #friendsongs-title (kept for the
           legacy hidden span used by other modules + accessibility), but
           the visible name lives inside #friend-detail-subnav now. -->
      <div id="friendsongs-title" style="display:none;"></div>
      <div id="friend-detail-subnav" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px 12px;padding:8px 16px 10px;border-bottom:1px solid var(--border);display:flex;flex-wrap:wrap;align-items:center;gap:6px;"></div>
      <!-- Tab body containers — only one visible at a time. Chat is NOT a
           tab here (separate surface; see #msg-thread inside
           #people-messages-page above). Drawer shows only share-viewer tabs. -->
      <div id="friend-detail-tab-songbook" class="friend-detail-tab" style="display:none;"></div>
      <div id="friend-detail-tab-trophies" class="friend-detail-tab" style="display:none;"></div>
      <div id="friend-detail-tab-perfstats" class="friend-detail-tab" style="display:none;"></div>
      <div id="friend-detail-tab-songbookstats" class="friend-detail-tab" style="display:none;"></div>
      <div id="friend-detail-tab-voicestats" class="friend-detail-tab" style="display:none;"></div>
      <div id="friend-detail-tab-perflog" class="friend-detail-tab" style="display:none;"></div>
    </div>

  </div>

  <!-- VENUES TAB -->
  <div id="tab-venues" class="panel" style="margin-top:-16px;">

    <!-- Venues tab Apple-fy Phase B (2026-06-08): 3 sub-views → 2.
         The 📍 Discover view collapses the old 🏠 Home Area + 🔍 Find Nearby
         (which were the SAME AI-by-zip engine wearing two hats — Gemini
         critique). ⭐ My Venues stays as the user's starred list, brutally
         simple (search + ＋ Add only — no power-filters; those belong on
         curated DMV browsing, not on Saved). The Set Tonight's Venue control
         lives ONLY in the global header chip (single source of truth) — no
         bridge chip on this tab, by design. Legacy view names alias to
         'discover' in setVenueView so popstate / ?venue= deep-links /
         closeVenueHomePage all still resolve. -->

    <!-- STICKY ZONE: 2-button sub-nav + per-view toolbar -->
    <div id="venue-sticky-zone" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:8px 16px 10px;border-bottom:1px solid var(--border);">
      <!-- Two-button sub-nav (was three). .active state is recomputed by
           setVenueView, so the static initial state below is just a
           first-paint placeholder. -->
      <div style="display:flex;gap:5px;margin-bottom:8px;">
        <button id="venue-sub-discover" class="tab-sub-btn active" onclick="setVenueView('discover',this)"
          style="flex:1;">📍 Discover</button>
        <button id="venue-sub-mine" class="tab-sub-btn" onclick="setVenueView('mine',this)"
          style="flex:1;">⭐ My Venues</button>
      </div>

      <!-- DISCOVER toolbar: Area pill (left) + Type chips (right) on row 1;
           search input on row 2. The "📍" pin glyph is INTENTIONALLY NOT used
           on the Area pill — that glyph belongs to the global tonight-venue
           chip in the header ("you are physically here"). Discover's pill
           speaks the LOOKING-AT-THIS-LOCATION language. -->
      <div id="venue-discover-sticky" style="display:none;">
        <div style="display:flex;gap:6px;align-items:center;margin-bottom:6px;flex-wrap:wrap;">
          <button id="venue-area-pill" class="area-pill area-pill-empty" onclick="openAreaSheet()">Set your area ⌄</button>
          <div id="venue-type-filter-row" style="display:flex;gap:5px;align-items:center;margin-left:auto;flex-wrap:wrap;"></div>
        </div>
        <input class="search-input" id="venues-search" type="text" placeholder="Search venues…" oninput="renderDmvVenues()"
          style="width:100%;display:block;box-sizing:border-box;">
      </div>

      <!-- MY VENUES toolbar — brutally simple (Gemini): search + ＋ Add. No
           day/area/access toggles, no Type chips. A Saved list is a personal
           favorites view, not a power-browse surface. -->
      <div id="venue-mine-sticky" style="display:none;">
        <div class="search-bar">
          <input class="search-input" type="text" placeholder="Search my venues…" oninput="renderMyVenues()" id="my-venues-search">
          <button class="btn btn-primary btn-sm" onclick="openAddVenue()">＋ Add</button>
        </div>
      </div>
    </div>

    <!-- First-session orientation card. Populated by _renderOrientCardInto
         on app load. Empty until then; auto-hides via display:none when the
         user has dismissed it (kk_orient_venues_dismissed). Restore via
         Settings → Start → "💡 Show orientation tips". -->
    <div id="orient-venues-container"></div>

    <!-- DISCOVER body — single host, dispatched by homeArea.type (dmv / zip /
         none) PLUS an optional transient _discoverZipOverride (peek mode).
         renderDiscoverBody() injects either the DMV list scaffolding, the
         AI-by-zip results container, or the empty state. -->
    <div id="venue-discover" style="display:none;padding-top:14px;"></div>

    <!-- MY VENUES list. Toolbar (search/add) lives in the sticky zone above
         as #venue-mine-sticky so it stays visible during scroll. -->
    <div id="venue-mine" style="padding-top:14px;">
      <div id="my-venues-list"></div>
    </div>

    <!-- VENUE HOME PAGE (featured venues only — UNTOUCHED). check-in, setlist
         builder, hot songs, community notes. openVenueHomePage hides the
         sticky zone; closeVenueHomePage restores it via setVenueView('discover'). -->
    <div id="venue-home-page" style="display:none;padding-top:14px;"></div>
  </div>

  <!-- STATS TAB -->
  <div id="tab-stats" class="panel" style="margin-top:-16px;">
    <!-- Sticky subnav -->
    <div id="stats-subnav" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:8px 16px 10px;border-bottom:1px solid var(--border);">
      <!-- Top row: the narrative surfaces. Overview is the default landing
           (Apple Health "Summary" model — lead with the story of your
           progress, not the raw ledger). Performance Log is one tap away
           here and via the Overview's "See all" — it's a drill-down now,
           not the entry point. -->
      <div style="display:flex;gap:5px;margin-bottom:5px;">
        <button class="tab-sub-btn active" onclick="setStatsView('overview',this)"
          style="flex:1;">📈 Overview</button>
        <button class="tab-sub-btn" onclick="setStatsView('perflog',this)"
          style="flex:1;">📋 Log</button>
        <button class="tab-sub-btn" onclick="setStatsView('trophy',this)"
          style="flex:1;">🏆 Trophies</button>
      </div>
      <!-- Bottom row: chart-based analytical surfaces. Grouped together
           because they all share the "viz selector + chart card" pattern. -->
      <div style="display:flex;gap:5px;">
        <button class="tab-sub-btn" onclick="setStatsView('perf',this)"
          style="flex:1;">📊 Performance</button>
        <button class="tab-sub-btn" onclick="setStatsView('songbook',this)"
          style="flex:1;">📚 Songbook</button>
        <button class="tab-sub-btn" onclick="setStatsView('voice',this)"
          style="flex:1;">🎙️ Voice</button>
      </div>
    </div>

    <!-- First-session orientation card. Populated by _renderOrientCardInto
         on app load. Empty until then; auto-hides via display:none when the
         user has dismissed it (kk_orient_stats_dismissed). Restore via
         Settings → Start → "💡 Show orientation tips". Sits above all
         sub-pages so it's visible regardless of which sub-view is active. -->
    <div id="orient-stats-container"></div>

    <!-- Vocal Tip surface. Populated by _renderVocalTipCard() on app load
         and on Stats tab open. Rotates one VOCAL_TECHNIQUES entry per
         session as a "💡 Vocal Tip" with tap → Vox Gym deep-link. Drives
         Vox Gym discovery from inside a high-traffic surface (Stats is
         where users come to track their growth, natural to introduce them
         to growth content). Dismissable per session via the ✕. -->
    <div id="vocal-tip-container"></div>

    <!-- Overview subpage — the DEFAULT landing (Apple Health "Summary"
         model). Leads with hero numbers (compounding, never-cadence — see
         the Frame-1 moat) + positive-only deltas, then a Recent-3 card that
         drills into the full Performance Log via "See all". Rendered by
         renderStatsOverview() in perf-modal.js. -->
    <div id="stats-overview-page" style="display:none;padding-top:14px;"></div>
    <!-- Trophy Room subpage -->
    <div id="trophy-room-page" style="display:none;padding-top:14px;">
      <div style="font-size:12px;color:var(--text2);margin-bottom:12px;line-height:1.5;">Unlock rewards by logging performances, tagging songs, trying new venues, and meeting new people.</div>
      <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:12px;">
        <div style="display:flex;gap:4px;flex:1;min-width:0;">
          <button id="tr-filter-all" class="btn btn-primary btn-sm" style="flex:1;justify-content:center;font-size:12px;" onclick="setTrophyFilter('all')">All</button>
          <button id="tr-filter-earned" class="btn btn-secondary btn-sm" style="flex:1;justify-content:center;font-size:12px;" onclick="setTrophyFilter('earned')">Earned only</button>
        </div>
        <select id="tr-sort" class="form-select" style="font-size:12px;padding:6px 8px;flex:1;min-width:0;" onchange="renderTrophyGrid()">
          <option value="default">Default order</option>
          <option value="az">A → Z</option>
          <option value="za">Z → A</option>
          <option value="newest">Newest unlock first</option>
          <option value="oldest">Oldest unlock first</option>
        </select>
        <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;color:var(--text2);white-space:nowrap;" title="Trophy pop-up notifications" onclick="toggleTrophyNotifs()">
          <span>🔔</span>
          <div id="tr-notif-track" class="ios-toggle-track" style="cursor:pointer;"></div>
        </label>
      </div>
      <div id="trophy-grid" class="trophy-grid"></div>
    </div>
    <!-- Performance Analysis subpage -->
    <div id="perf-analytics-page" style="display:none;padding-top:14px;">
      <div style="font-size:12px;color:var(--text2);margin-bottom:12px;line-height:1.5;">Performances by period and visual breakdowns from your logged performance history.</div>
      <div class="viz-selector" style="margin-bottom:16px;" id="studio-viz-selector">
        <button class="studio-viz-btn active" onclick="setStudioViz('tour',this)" data-viz="tour">📅 Schedule</button>
        <button class="studio-viz-btn" onclick="setStudioViz('vibe',this)" data-viz="vibe">⚡ Vibe</button>
        <button class="studio-viz-btn" onclick="setStudioViz('ratings',this)" data-viz="ratings">🌟 Ratings</button>
        <button class="studio-viz-btn" onclick="setStudioViz('hot100',this)" data-viz="hot100">🔥 Hot 100</button>
        <button class="studio-viz-btn" onclick="setStudioViz('venues',this)" data-viz="venues">📍 Venues</button>
        <button class="studio-viz-btn" onclick="setStudioViz('momentum',this)" data-viz="momentum">📆 Momentum</button>
        <button class="studio-viz-btn" onclick="setStudioViz('reach',this)" data-viz="reach">🎤 Artist Reach</button>
        <button class="studio-viz-btn" onclick="setStudioViz('period',this)" data-viz="period">📊 By Period</button>
      </div>
      <!-- Hot 100 window selector — shown only for hot100 viz -->
      <div id="hot100-window-row" style="display:none;gap:6px;flex-wrap:wrap;margin-bottom:12px;align-items:center;">
        <span style="font-size:11px;color:var(--text3);font-weight:600;">Compare:</span>
        <button class="chip active" onclick="setHot100Window(7,this)">7 days</button>
        <button class="chip" onclick="setHot100Window(14,this)">14 days</button>
        <button class="chip" onclick="setHot100Window(30,this)">30 days</button>
        <button class="chip" onclick="setHot100Window(90,this)">90 days</button>
      </div>
      <div id="studio-chart-card" class="chart-card" style="margin-bottom:20px;">
        <button class="chart-fs-btn" onclick="toggleChartFS('studio-chart-card',()=>studioChartInstance)" title="Fullscreen">⛶</button>
        <div id="studio-chart-title" style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;color:var(--text2);margin-bottom:12px;"></div>
        <!-- Canvas for Chart.js charts -->
        <div id="studio-canvas-wrap" class="chart-canvas-wrap" style="position:relative;max-height:320px;">
          <canvas id="studio-chart" style="max-width:100%;"></canvas>
        </div>
        <!-- Billboard-style HTML list for Hot 100 -->
        <div id="studio-hot100-list" style="display:none;"></div>
        <div id="studio-chart-desc" style="font-size:11px;color:var(--text3);margin-top:10px;line-height:1.5;"></div>
      </div>
      <!-- By Period view — shown only when studioViz === 'period' -->
      <div id="stats-period-panel" style="display:none;">
        <div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px;" id="stats-period-selector">
          <button class="chip active" onclick="setStatsPeriod(7,this)">Last Week</button>
          <button class="chip" onclick="setStatsPeriod(30,this)">Last Month</button>
          <button class="chip" onclick="setStatsPeriod(90,this)">Last 3 Months</button>
          <button class="chip" onclick="setStatsPeriod(180,this)">Last 6 Months</button>
          <button class="chip" onclick="setStatsPeriod(365,this)">Last Year</button>
          <button class="chip" onclick="setStatsPeriod(99999,this)">All Time</button>
        </div>
        <div id="stats-detail-list" style="margin-bottom:16px;"></div>
      </div>
    </div>
    <!-- Songbook Analysis subpage -->
    <div id="vocal-portfolio-page" style="display:none;padding-top:14px;">
      <div style="font-size:12px;color:var(--text2);margin-bottom:12px;line-height:1.5;">Visual breakdowns of your songbook — genre spread, mastery progress, artist bench, and more.</div>
      <div class="viz-selector" style="margin-bottom:16px;" id="vocal-viz-selector">
        <button class="studio-viz-btn active" onclick="setVocalViz('genremap',this)">🎸 Genre Map</button>
        <button class="studio-viz-btn" onclick="setVocalViz('mastery',this)">🎯 Mastery</button>
        <button class="studio-viz-btn" onclick="setVocalViz('artists',this)">👤 Top Artists</button>
        <button class="studio-viz-btn" onclick="setVocalViz('vibedna',this)">🧬 Vibe DNA</button>
        <button class="studio-viz-btn" onclick="setVocalViz('themedna',this)">🎭 Theme DNA</button>
        <button class="studio-viz-btn" onclick="setVocalViz('growth',this)">📈 Growth</button>
      </div>
      <div id="vocal-chart-card" class="chart-card">
        <button class="chart-fs-btn" onclick="toggleChartFS('vocal-chart-card',()=>vocalChartInstance)" title="Fullscreen">⛶</button>
        <div id="vocal-chart-title" style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;color:var(--text2);margin-bottom:12px;"></div>
        <div class="chart-canvas-wrap" style="position:relative;max-height:360px;">
          <canvas id="vocal-chart" style="max-width:100%;"></canvas>
        </div>
        <div id="vocal-chart-desc" style="font-size:11px;color:var(--text3);margin-top:10px;line-height:1.5;"></div>
      </div>
    </div>
    <!-- Voice Analysis subpage -->
    <div id="voicechars-page" style="display:none;padding-top:14px;">
      <div style="font-size:12px;color:var(--text2);margin-bottom:12px;line-height:1.5;">Vocal range, techniques, and register patterns drawn from your tagged songs.</div>
      <div class="viz-selector" style="margin-bottom:16px;" id="voicechars-viz-selector">
        <button class="studio-viz-btn active" onclick="setVoiceCharsViz('range',this)">🎤 Range Profile</button>
        <button class="studio-viz-btn" onclick="setVoiceCharsViz('my-techniques',this)">💪 My Techniques</button>
        <button class="studio-viz-btn" onclick="setVoiceCharsViz('my-register',this)">↕️ My Register</button>
        <button class="studio-viz-btn" onclick="setVoiceCharsViz('perf-techniques',this)">🎵 Perf Techniques</button>
        <button class="studio-viz-btn" onclick="setVoiceCharsViz('catalog-techniques',this)">🌐 Catalog Freq</button>
      </div>
      <div id="voicechars-chart-card" class="chart-card">
        <button class="chart-fs-btn" onclick="toggleChartFS('voicechars-chart-card',()=>voiceCharsChartInstance)" title="Fullscreen">⛶</button>
        <div id="voicechars-title" style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;color:var(--text2);margin-bottom:12px;"></div>
        <div class="chart-canvas-wrap" style="position:relative;max-height:380px;">
          <canvas id="voicechars-chart" style="max-width:100%;"></canvas>
        </div>
        <div id="voicechars-desc" style="font-size:11px;color:var(--text3);margin-top:10px;line-height:1.5;"></div>
      </div>
    </div>

    <!-- Performance Log subpage -->
    <div id="perflog-page" style="display:none;padding-top:14px;">
      <div style="font-size:12px;color:var(--text2);margin-bottom:10px;line-height:1.5;">Every performance by date, venue &amp; song.</div>
      <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:18px;">
        <div style="font-size:11px;color:var(--text);padding:0 2px;">✏️ Tap entry to edit</div>
        <button class="btn btn-primary btn-sm" onclick="openAddPerfEntry()">+ Add Entry</button>
      </div>
      <div id="perflog-list"></div>
    </div>

    <!-- EDIT PERF ENTRY MODAL -->
    <!-- Uses canonical .modal-overlay + .modal + .modal-handle so the
         swipe-to-close handler (modal-swipe.js _initModalSwipe) picks it up
         automatically. The handle was historically present visually but the
         old inline display:flex/none pattern bypassed the swipe wiring. -->
    <div class="modal-overlay" id="edit-perf-modal" onclick="if(event.target===this)closeEditPerfEntry()">
      <div class="modal">
        <div class="modal-handle"></div>
        <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;">
          <div style="font-family:'Bebas Neue',sans-serif;font-size:18px;letter-spacing:1px;">Edit Entry</div>
          <button onclick="deleteEditPerfEntry()" title="Delete entry" aria-label="Delete entry" style="background:none;border:1px solid rgba(232,57,90,0.3);border-radius:6px;color:var(--accent);font-size:14px;padding:4px 8px;cursor:pointer;line-height:1;">🗑</button>
        </div>

        <!-- Row 1: Date & Time | Venue -->
        <div class="form-group" style="display:flex;gap:12px;">
          <div style="flex:1;min-width:0;">
            <label class="form-label">Date &amp; Time</label>
            <input type="datetime-local" id="epe-datetime" class="form-input" style="width:100%;">
          </div>
          <div style="flex:1;min-width:0;">
            <label class="form-label">Venue</label>
            <div style="position:relative;">
              <input class="form-input" id="epe-venue" autocomplete="off"
                placeholder="— no venue —" style="width:100%;">
              <div id="epe-venue-suggestions" class="autocomplete-dropdown"
                style="display:none;position:absolute;top:100%;left:0;right:0;z-index:300;background:var(--surface);border:1px solid var(--border);border-radius:8px;margin-top:4px;max-height:240px;overflow-y:auto;"></div>
            </div>
          </div>
        </div>

        <!-- Row 2: Type | Duet checkbox (styled to match select height) -->
        <div class="form-group" style="display:flex;gap:12px;align-items:flex-end;">
          <div style="flex:1;min-width:0;">
            <label class="form-label">Type</label>
            <select id="epe-type" class="form-select" style="width:100%;">
              <option value="karaoke">🎤 Karaoke</option>
              <option value="open_mic">🎙️ Open Mic</option>
            </select>
          </div>
          <label style="flex:1;display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:13px;color:var(--text);background:var(--surface2);min-width:0;">
            <input type="checkbox" id="epe-duet" style="margin:0;width:18px;height:18px;cursor:pointer;accent-color:#f472b6;flex-shrink:0;">
            <span style="color:#f472b6;font-size:16px;line-height:1;flex-shrink:0;">♫</span>
            <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">Duet</span>
          </label>
        </div>

        <!-- Row 3: Rating (tap active chip to clear; — Skip retired) -->
        <div class="form-group">
          <label class="form-label">How'd it go?</label>
          <div id="epe-rating-row" style="display:flex;gap:6px;flex-wrap:wrap;">
            <div class="chip" data-rating="crushed" onclick="epeToggleRating('crushed')" title="Tap again to clear" style="padding:5px 11px;font-size:12px;cursor:pointer;">💪 Crushed it</div>
            <div class="chip" data-rating="solid" onclick="epeToggleRating('solid')" title="Tap again to clear" style="padding:5px 11px;font-size:12px;cursor:pointer;">✓ Solid</div>
            <div class="chip" data-rating="off" onclick="epeToggleRating('off')" title="Tap again to clear" style="padding:5px 11px;font-size:12px;cursor:pointer;">😬 Off-night</div>
          </div>
        </div>

        <!-- Row 4: Song — unified search (library → iTunes → manual footer).
             Same pattern as Add Perf Entry. Save semantics differ though:
               • Library pick (different song) → MOVES this perf
               • Manual mode (footer or iTunes pick) → RENAMES current song -->
        <div class="form-group">
          <label class="form-label">Song</label>
          <input type="text" id="epe-search" class="form-input" placeholder="Search your library…" autocomplete="off" oninput="epeRenderResults()" style="margin-bottom:8px;">
          <div id="epe-selected" style="display:none;margin-bottom:8px;color:var(--success);font-size:12px;line-height:1.4;padding:6px 10px;background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.2);border-radius:6px;"></div>
          <div id="epe-results" style="max-height:240px;overflow-y:auto;border:1px solid var(--border);border-radius:var(--radius-sm);"></div>
          <!-- Manual entry — revealed by "Edit title/artist" footer in results.
               Pre-filled with current song's title + artist on modal open. -->
          <div id="epe-manual" style="display:none;margin-top:8px;">
            <div style="font-size:11px;color:var(--text3);margin-bottom:6px;font-style:italic;">Edit the song's title and artist (renames the song in your library):</div>
            <div style="display:flex;gap:6px;">
              <input type="text" id="epe-manual-title" class="form-input" placeholder="Song Title" style="flex:1;padding:5px 10px;font-size:13px;">
              <input type="text" id="epe-manual-artist" class="form-input" placeholder="Artist" style="flex:1;padding:5px 10px;font-size:13px;">
            </div>
          </div>
        </div>

        <div style="display:flex;gap:8px;margin-top:4px;">
          <button class="btn btn-primary" style="flex:1;justify-content:center;" onclick="saveEditPerfEntry()">Save</button>
          <button class="btn btn-secondary" style="flex:1;justify-content:center;" onclick="closeEditPerfEntry()">Cancel</button>
        </div>
      </div>
    </div>

    <!-- ADD PERF ENTRY MODAL -->
    <!-- Same canonical-modal pattern as edit-perf-modal above; swipe-to-close
         fires from the handle. -->
    <div class="modal-overlay" id="add-perf-modal" onclick="if(event.target===this)closeAddPerfEntry()">
      <div class="modal">
        <div class="modal-handle"></div>
        <div style="font-family:'Bebas Neue',sans-serif;font-size:22px;letter-spacing:1px;margin-bottom:16px;">Add Performance Entry</div>

        <div class="form-group">
          <label class="form-label">Date &amp; Time</label>
          <input type="datetime-local" id="ape-datetime" class="form-input">
        </div>

        <div class="form-group">
          <label class="form-label">Venue</label>
          <div style="position:relative;">
            <input class="form-input" id="ape-venue" autocomplete="off"
              placeholder="— no venue —" style="width:100%;">
            <div id="ape-venue-suggestions" class="autocomplete-dropdown"
              style="display:none;position:absolute;top:100%;left:0;right:0;z-index:300;background:var(--surface);border:1px solid var(--border);border-radius:8px;margin-top:4px;max-height:240px;overflow-y:auto;"></div>
          </div>
        </div>

        <div class="form-group">
          <label class="form-label">Type</label>
          <select id="ape-type" class="form-select">
            <option value="karaoke">🎤 Karaoke</option>
            <option value="open_mic">🎙️ Open Mic</option>
          </select>
        </div>

        <!-- Unified Song search: filter library → iTunes fallback → manual.
             Same pattern as Quick Log. Replaces the older From Library /
             Enter Manually tab toggle that confused users (the iPhone
             user reported "doesn't autocomplete like everywhere else"
             before this consolidation). -->
        <div class="form-group">
          <label class="form-label">Song</label>
          <input type="text" id="ape-search" class="form-input" placeholder="Songbook or manual…" autocomplete="off" oninput="apeRenderResults()" style="margin-bottom:8px;">
          <div id="ape-selected" style="display:none;margin-bottom:8px;font-size:12px;color:var(--success);padding:6px 10px;background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.2);border-radius:6px;"></div>
          <div id="ape-results" style="max-height:240px;overflow-y:auto;border:1px solid var(--border);border-radius:var(--radius-sm);"></div>
          <!-- Manual-entry block, revealed by tapping the "Log manually" footer
               in the results panel above. Hidden by default. -->
          <div id="ape-manual" style="display:none;margin-top:8px;">
            <div style="font-size:11px;color:var(--text3);margin-bottom:6px;font-style:italic;">Not in your songbook — enter manually:</div>
            <div style="display:flex;gap:6px;margin-bottom:6px;">
              <input type="text" id="ape-manual-title" class="form-input" placeholder="Song Title" style="flex:1;padding:5px 10px;font-size:13px;">
              <input type="text" id="ape-manual-artist" class="form-input" placeholder="Artist" style="flex:1;padding:5px 10px;font-size:13px;">
            </div>
            <!-- Tap a button to flag "also add to library as X" — visible
                 toggle, no commit. Tap Save below to commit the perf
                 (and any flagged library add). -->
            <div style="display:flex;align-items:center;gap:6px;margin-top:4px;font-size:11px;color:var(--text3);">
              <span>Also add to library:</span>
              <button id="ape-add-rep-btn" type="button" onclick="_apeToggleLibraryAdd('repertoire')"
                style="font-size:11px;font-weight:600;padding:4px 9px;border-radius:12px;border:1px solid var(--success);background:transparent;color:var(--success);cursor:pointer;font-family:inherit;">+ Rep</button>
              <button id="ape-add-ws-btn" type="button" onclick="_apeToggleLibraryAdd('working')"
                style="font-size:11px;font-weight:600;padding:4px 9px;border-radius:12px;border:1px solid var(--warning);background:transparent;color:var(--warning);cursor:pointer;font-family:inherit;">+ WS</button>
            </div>
          </div>
        </div>

        <div style="display:flex;gap:8px;margin-top:4px;">
          <button class="btn btn-primary" style="flex:1;justify-content:center;" onclick="saveAddPerfEntry()">Save Entry</button>
          <button class="btn btn-secondary" style="flex:1;justify-content:center;" onclick="closeAddPerfEntry()">Cancel</button>
        </div>
      </div>
    </div>
    <!-- Hidden elements still needed for JS -->
    <div id="stats-grid" style="display:none;"></div>
    <div id="leaderboard" style="display:none;"></div>
    <div id="revisit-list" style="display:none;"></div>
  </div>

  <!-- SETTINGS TAB -->

  <!-- ARTIST CATALOG TAB -->
  <div id="tab-artists" class="panel">
    <div style="padding:12px 14px 8px;">
      <div style="font-family:'Bebas Neue',sans-serif;font-size:22px;letter-spacing:1px;color:var(--text);margin-bottom:8px;">🎙️ FAMOUS VOICE PROFILES <span style="font-size:14px;font-weight:400;opacity:0.7;">(How Do They Do That?)</span></div>
      <div style="font-size:12px;color:var(--text3);margin-bottom:10px;">How famous singers do what they do — technique profiles for 600+ artists. Tap any name to expand.</div>

      <!-- Edit mode banner (only shown when ?edit=1) -->
      <div id="edit-mode-banner" style="display:none;background:rgba(109,131,242,0.08);border:1px solid rgba(109,131,242,0.3);border-radius:8px;padding:12px 14px;margin-bottom:10px;">

        <!-- Row 1: title + export -->
        <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;margin-bottom:10px;">
          <div>
            <div style="font-size:10px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:#6d83f2;margin-bottom:2px;">✏️ EDIT MODE</div>
            <div id="edit-override-count" style="font-size:11px;color:var(--text3);">0 unsaved edits</div>
          </div>
          <div style="display:flex;gap:6px;flex-wrap:wrap;">
          <button onclick="exportArtistDB()" style="padding:6px 14px;border-radius:8px;border:1px solid rgba(109,131,242,0.5);background:rgba(109,131,242,0.15);color:#6d83f2;font-size:12px;font-weight:700;cursor:pointer;">⬇ Export artist_db.js</button>
          <button onclick="toggleExpandAllVgp(this)" style="padding:6px 14px;border-radius:8px;border:1px solid rgba(56,189,248,0.4);background:rgba(56,189,248,0.08);color:#38bdf8;font-size:12px;font-weight:700;cursor:pointer;">⊞ Expand All</button>
          <button onclick="clearEditsOnly()" style="padding:6px 14px;border-radius:8px;border:1px solid rgba(232,57,90,0.3);background:rgba(232,57,90,0.06);color:#f87171;font-size:12px;font-weight:700;cursor:pointer;">✕ Clear edits</button>
        </div>
        </div>

        <!-- Row 2: batch controls -->
        <div style="border-top:1px solid rgba(109,131,242,0.2);padding-top:10px;">
          <div style="font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--text3);margin-bottom:8px;">Batch AI Tagging</div>
          <div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px;">
            <div style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--text3);margin-bottom:4px;">With grounding (slower, obscure artists)</div>
            <div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px;">
              <button onclick="startBatchTag('new', true)" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(45,212,191,0.4);background:rgba(45,212,191,0.08);color:#2dd4bf;font-size:11px;font-weight:600;cursor:pointer;">🌐 Tag untagged</button>
              <button onclick="startBatchTag('all', true)" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(244,114,182,0.4);background:rgba(244,114,182,0.08);color:#f472b6;font-size:11px;font-weight:600;cursor:pointer;">🌐 Overwrite all</button>
            </div>
            <div style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--text3);margin-bottom:4px;">No grounding (faster, well-known artists)</div>
            <div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px;">
              <button onclick="startBatchTag('new', false)" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(45,212,191,0.4);background:rgba(45,212,191,0.08);color:#2dd4bf;font-size:11px;font-weight:600;cursor:pointer;">⚡ Tag untagged</button>
              <button onclick="startBatchTag('all', false)" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(244,114,182,0.4);background:rgba(244,114,182,0.08);color:#f472b6;font-size:11px;font-weight:600;cursor:pointer;">⚡ Overwrite all</button>
            </div>
            <div style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--text3);margin-bottom:4px;">Group members (boy bands, R&amp;B groups, etc. — fills the FVP gaps where members aren't separate solo entries)</div>
            <div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px;">
              <button onclick="startBatchTagGroups(false)" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(168,85,247,0.4);background:rgba(168,85,247,0.08);color:#c084fc;font-size:11px;font-weight:600;cursor:pointer;">🎭 Tag empty groups (fast)</button>
              <button onclick="startBatchTagGroups(true)" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(168,85,247,0.4);background:rgba(168,85,247,0.08);color:#c084fc;font-size:11px;font-weight:600;cursor:pointer;">🌐 Tag empty groups (grounded)</button>
            </div>
            <button onclick="clearAllVoiceProfiles()" style="padding:5px 12px;border-radius:7px;border:1px solid rgba(232,57,90,0.3);background:rgba(232,57,90,0.06);color:#f87171;font-size:11px;font-weight:600;cursor:pointer;">🗑 Clear all tags</button>
          </div>
          <!-- Progress area -->
          <div id="batch-progress-area" style="display:none;">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;">
              <div id="batch-status" style="font-size:11px;color:var(--text2);"></div>
              <button onclick="stopBatchTag()" style="padding:3px 8px;border-radius:5px;border:1px solid var(--border);background:transparent;color:var(--text3);font-size:10px;font-weight:600;cursor:pointer;">⏹ Stop</button>
            </div>
            <div style="background:var(--surface2);border-radius:4px;height:6px;overflow:hidden;">
              <div id="batch-progress-bar" style="height:100%;background:#6d83f2;border-radius:4px;transition:width 0.3s;width:0%;"></div>
            </div>
            <div id="batch-log" style="font-size:10px;color:var(--text3);margin-top:6px;max-height:60px;overflow-y:auto;line-height:1.6;"></div>
          </div>
        </div>

      </div>

      <!-- Search + filter row -->
      <div style="display:flex;gap:8px;margin-bottom:8px;">
        <input id="artist-search" type="text" placeholder="Search artists…" oninput="renderArtistCatalog()"
          style="flex:1;background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:7px 10px;font-size:13px;color:var(--text);" />
        <select id="artist-filter-vox" onchange="renderArtistCatalog()"
          style="background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:6px 8px;font-size:12px;color:var(--text);min-width:70px;">
          <option value="">All ↕</option>
          <option value="↑">↑ High</option>
          <option value="→">→ Mid</option>
          <option value="↓">↓ Low</option>
        </select>
        <select id="artist-sort-order" onchange="renderArtistCatalog()"
          style="background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:6px 8px;font-size:12px;color:var(--text);min-width:60px;">
          <option value="az">A→Z</option>
          <option value="za">Z→A</option>
        </select>
      </div>

      <!-- Technique tag filter chips -->
      <div id="artist-tag-filter-row" style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px;"></div>

      <div id="artist-catalog-list" style="display:flex;flex-direction:column;gap:6px;"></div>
      <div id="artist-catalog-empty" style="display:none;text-align:center;padding:32px;color:var(--text3);font-size:13px;">No artists match your filter.</div>
    </div>
  </div>

  <!-- MORE panel (2026-06-07 IA reorg) — the honest "everything that isn't a main
       tab or a genuine Setting" hub, reached from the header ☰ button. Settings
       got its OWN header icon (⚙️) holding just config, so it's NOT a row here.
       Two tiers of "explain BAR to me": FAST orientation (Walkthrough, Highlight
       Reel) vs. DEEP reference (Help). Page-rows route via openMorePage() (shows
       the page in the settings host with a "← More" back bar); action-rows fire a
       modal directly. -->
  <div id="tab-more" class="panel" style="margin-top:-16px;">
    <!-- Heading doubles as the exit (2026-06-07): More is reached from the header ☰
         and is a full panel with no sibling tab to switch from, so it needed an
         explicit way back. The whole row is tappable (the blank space beside the
         title closes too) with a visible ✕ as the affordance; closeMore() returns
         to whatever tab you were on before opening More. -->
    <div class="more-head" onclick="closeMore()" role="button" tabindex="0" title="Close — back to where you were">
      <span class="more-head-title">More</span>
      <span class="more-head-close" aria-label="Close More">✕</span>
    </div>
    <div style="display:flex;flex-direction:column;gap:8px;">
      <!-- The three "explain BAR" rows are ordered by ASCENDING time commitment
           (lazy-user-first): skim → read → full reference. -->
      <button class="more-row" onclick="openSizzleReel()"><span class="more-row-i">▶</span><span class="more-row-tx"><span class="more-row-t">Highlight Reel</span><span class="more-row-s">Quickest look — a visual tour, ~1 min</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openMorePage('sp-start')"><span class="more-row-i">🚀</span><span class="more-row-tx"><span class="more-row-t">Walkthrough</span><span class="more-row-s">Step-by-step guide to every feature</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openHelpModal()"><span class="more-row-i">❓</span><span class="more-row-tx"><span class="more-row-t">Help &amp; Guide</span><span class="more-row-s">The full reference — for when you're stuck</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openMorePage('sp-install')"><span class="more-row-i">📲</span><span class="more-row-tx"><span class="more-row-t">Install BAR as an App</span><span class="more-row-s">Add to your home screen</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openMorePage('sp-yourdata')"><span class="more-row-i">💾</span><span class="more-row-tx"><span class="more-row-t">Your Data</span><span class="more-row-s">Backup, restore, diagnose &amp; clear</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openMorePage('sp-tags')"><span class="more-row-i">🏷️</span><span class="more-row-tx"><span class="more-row-t">Tags &amp; Tools</span><span class="more-row-s">Auto-enrich, KaraFun, custom tags</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openMorePage('sp-about')"><span class="more-row-i">ℹ️</span><span class="more-row-tx"><span class="more-row-t">About</span><span class="more-row-s">Version, data sources &amp; credits</span></span><span class="more-row-x">›</span></button>
      <button class="more-row" onclick="openMorePage('sp-bug')"><span class="more-row-i">🐛</span><span class="more-row-tx"><span class="more-row-t">Report a Bug</span><span class="more-row-s">Tell us what broke</span></span><span class="more-row-x">›</span></button>
    </div>
  </div>

  <div id="tab-settings" class="panel" style="margin-top:-16px;">

  <!-- ═══ SETTINGS SUB-NAV (config only — 2026-06-07 IA reorg) ════════════════════
       Slimmed from 6 buttons to the two genuine-config pages (Profile · Account).
       Start/Tags/Report/About were not "settings" — they moved to the ☰ More hub
       and are reached via openMorePage() (which shows the page with a "← More"
       back bar instead of this sub-nav; see showSettingsPage's chrome toggle). -->
  <div id="settings-subnav" style="position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px;padding:8px 16px 10px;border-bottom:1px solid var(--border);display:grid;grid-template-columns:repeat(2,1fr);gap:6px;margin-bottom:0;">
    <button class="settings-nav-btn active" onclick="showSettingsPage('sp-profile',this)" style="">🎤 Profile</button>
    <button class="settings-nav-btn" onclick="showSettingsPage('sp-account',this)" style="">🔒 Account</button>
  </div>

  <!-- "← More" back bar — shown (in place of the sub-nav) when a More-hub page
       (Walkthrough / Tags / About / Report) is being viewed inside this panel.
       Toggled by showSettingsPage based on page type. -->
  <div id="more-back-bar" style="display:none;position:sticky;top:var(--tab-bar-bottom,100px);z-index:50;background:var(--bg);margin:0 -16px 0;padding:8px 16px 10px;border-bottom:1px solid var(--border);">
    <button class="tab-sub-btn" onclick="backToMore()" style="font-size:12px;">← More</button>
  </div>

  <!-- ═══ SETTINGS SEARCH ══════════════════════════════════════════════════ -->
  <div id="settings-search-wrap" style="padding:10px 0 2px;">
    <div style="position:relative;">
      <input id="settings-search-input" type="search" autocomplete="off"
        placeholder="Search settings"
        style="width:100%;box-sizing:border-box;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px 8px 12px;font-size:13px;color:var(--text);font-family:'DM Sans',sans-serif;outline:none;"
        oninput="clearTimeout(_ssTimer);_ssTimer=setTimeout(()=>runSettingsSearch(this.value),180)"
        onkeydown="if(event.key==='Escape'){this.value='';runSettingsSearch('');}">
    </div>
    <div id="settings-search-results" style="display:none;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);margin-top:4px;overflow:hidden;"></div>
  </div>

  <!-- ═══ PAGE: GETTING STARTED / WALKTHROUGH (now a ☰ More page, 2026-06-07) ═══
       Default-hidden; reached via More → Walkthrough (openMorePage('sp-start')),
       which shows it with a "← More" back bar instead of the Settings sub-nav. -->
  <div id="sp-start" class="settings-page" style="display:none;">
    <div class="settings-section">
      <div class="settings-title">🚀 Getting Started</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.7;">
        <p style="margin:0 0 12px;"><b style="color:var(--text);">Building A Rockstar</b> is for anyone who sings — whether you're a karaoke regular with a tight go-to list, an open-mic singer building a real set, or someone trying to take their voice further. If you sing, this is for you.</p>
        <p style="margin:0 0 12px;">The free tier handles the busywork of being a singer: a smart, taggable songbook; setlists you can build and reorder for tonight's venue; a performance log that remembers what you sang where; AI that finds new songs you'll love; and crowd-tested picks from your favorite spots. No account required.</p>
        <p style="margin:0 0 12px;">For singers who want to keep growing, BAR believes <b style="color:var(--text);">the stage is your training ground</b>. Karaoke and open-mic nights give you a real audience, real nerves, and real feedback — and every performance you log becomes data you can learn from. The paid tier goes deeper with personalized voice analysis and exercises matched to your actual voice, but it's there when you want it, not in the way when you don't.</p>
        <p style="margin:0;color:var(--accent2);font-weight:600;text-align:center;font-size:14px;">Go ahead. Build A Rockstar.</p>
      </div>
    </div>

    <!-- ❓ Help & Guide — relocated from the header (2026-06-06). The header was
         trimmed to 3 labeled icons (Find · Settings · Account); Help lives here
         now (and is still reachable via 🔍 Find → "help"). -->
    <div class="settings-section">
      <div class="settings-title">❓ Help &amp; Guide</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:12px;">
        The full how-it-works guide — every feature, explained. Also reachable any time from the 🔍 Find button up top (just type &ldquo;help&rdquo;).
      </div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="openHelpModal()">❓ Open Help &amp; Guide</button>
    </div>

    <!-- Install as an App moved to its own ☰ More page (sp-install, 2026-06-07). -->

    <!-- ─── Highlight Reel section (mirrors Help modal section 2) ───────────── -->

    <div class="settings-section">
      <div class="settings-title">▶ The Highlight Reel</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        A quick visual tour of everything BAR can do — no how-tos, just the highlights. Tap through to skim the features, then dig into the Full Walkthrough below to learn the deep moves.
      </div>
      <button class="btn btn-primary btn-sm" onclick="openSizzleReel()" style="width:100%;justify-content:center;">▶ Launch the Highlight Reel</button>
    </div>

    <!-- ─── Full Walkthrough section divider ──────────────────────────────── -->
    <div style="font-family:'Bebas Neue',sans-serif;font-size:20px;letter-spacing:2px;color:var(--text2);margin:18px 0 4px;text-align:center;border-top:1px solid var(--border);padding-top:16px;">FULL WALKTHROUGH</div>
    <div style="font-size:12px;color:var(--text3);text-align:center;margin-bottom:14px;font-style:italic;">Everything below is a step-by-step guide to the app's main features.</div>

    <!-- ─── First-time walkthrough ────────────────────────────────────────── -->

    <div class="settings-section">
      <div class="settings-title">🎵 Add your songs</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:14px;">
        Your songbook is the heart of BAR — it's where you build the repertoire you'll perform from. Three ways in:
      </div>

      <!-- Option 1: Add one at a time -->
      <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:6px;">➕ One at a time</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:8px;">
        Opens the full song editor — title, artist, key, tags, notes.
      </div>
      <button class="btn btn-primary btn-sm" onclick="openAddSong()" style="width:100%;justify-content:center;margin-bottom:18px;">＋ Add a Song</button>

      <!-- Option 2: Bulk import. Lives inside the Add Song modal as of 2026-05-26
           — Settings → Start now just points users there so the bulk affordance
           is discoverable from the same place users go when thinking "add song." -->
      <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:6px;">📋 Bulk import</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:10px;line-height:1.6;">
        Paste a song list, or attach .txt files / screenshots — AI handles the messy formatting. Lives inside the ＋ Add Song modal on the "📋 Bulk Add" tab.
      </div>
      <button class="btn btn-primary btn-sm" onclick="openBulkAddSong()" style="width:100%;justify-content:center;margin-bottom:18px;">📋 Open Bulk Add →</button>

      <!-- Option 3: Song ID -->
      <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:6px;">🎵 Song ID</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:18px;">
        At a karaoke night and don't know what's playing? Tap the cyan 🔵 button on the audio FAB (lower-left mic icon) to identify whatever's in the air and add it directly to your songbook.
      </div>

      <!-- View Songbook footer -->
      <div style="border-top:1px solid var(--border);padding-top:12px;">
        <div style="font-size:13px;color:var(--text2);margin-bottom:8px;line-height:1.6;">
          View the songs you added here:
        </div>
        <button class="btn btn-secondary btn-sm" onclick="showTab('songs');" style="width:100%;justify-content:center;">View Songbook →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">🏷️ Organize with filter tags</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:12px;">
        Tag songs by <b style="color:var(--text);">performance style</b> (Anthem, Slow Ballad, Showstopper, etc.), <b style="color:var(--text);">genre</b>, <b style="color:var(--text);">era</b> (decade), <b style="color:var(--text);">theme</b> (what the song is about), and <b style="color:var(--text);">voice requirements</b>. Then combine filters to dial up exactly what you want to sing on a given night — e.g. "90s rock songs that need a big belt and crowd energy."
      </div>

      <!-- Demo filter drawer — same component used on the Songbook tab, but populated
           with a sample songbook and isolated state. Tapping doesn't affect anything. -->
      <button class="btn btn-sm" id="demo-tag-filter-btn" onclick="toggleDemoTagFilterDrawer()"
        style="width:100%;justify-content:center;padding:4px 2px;font-size:11px;border-radius:20px;background:rgba(34,211,238,0.1);border:1px solid rgba(34,211,238,0.35);color:#67e8f9;">＋ Tag Filters</button>
      <div id="demo-tag-filter-drawer" style="display:none;margin-top:8px;">
        <div style="font-size:11px;color:var(--warning);background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.35);border-radius:6px;padding:6px 8px;margin-bottom:8px;text-align:center;font-weight:600;">👀 Preview only — this drawer is the real Songbook filter, populated with a sample songbook. Toggling chips here does nothing to your actual songbook.</div>
        <div style="font-size:11px;color:var(--text3);margin-bottom:8px;line-height:1.6;">Tap a category to open it, then tap chips to filter. Within a category, pick OR or AND with the toggle; between categories it's always AND. The match count updates live as you toggle.</div>
        <div id="demo-filter-count" style="font-size:11px;color:var(--text3);text-align:right;margin-bottom:6px;font-weight:600;">5 of 5 songs</div>
        <div id="demo-tag-filters"></div>
        <div style="text-align:right;margin-top:8px;">
          <span onclick="toggleDemoTagFilterDrawer()" style="font-size:12px;color:var(--text3);cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;">▲ Close filters</span>
        </div>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">🤖 Auto-Enrich Songbook</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:12px;">
        Lazy? Us too. <b style="color:var(--text);">BAR auto-enriches every song you add</b> — automatically, in the background, no button-clicking. The data lands in waves: metadata instantly, the AI tags a few seconds later. The Settings → Tags panel is just the <em>catch-up</em> for older songs (or to re-check KaraFun after you upload a fresh CSV).
      </div>
      <ul style="font-size:13px;color:var(--text2);line-height:1.7;margin:0 0 14px;padding-left:22px;">
        <li><b style="color:var(--text);">Metadata</b> — genre, album art, release year, musical key &amp; BPM (auto-applies Genre + Era tags).</li>
        <li><b style="color:var(--text);">Performance tags</b> — what the song <em>does in a room</em> (Slow Ballad, Showstopper, Fun/Dance…).</li>
        <li><b style="color:var(--text);">Theme tags</b> — what the song is <em>about</em> (Heartbreak, Defiance, Nostalgia…).</li>
        <li><b style="color:var(--text);">Voice profile</b> — searchable by the artist's register, resonance, texture &amp; phrasing (our 823-artist Famous Voice Profiles database).</li>
        <li><b style="color:var(--text);">KaraFun availability</b> — flags which of your songs are in your uploaded KaraFun catalog.</li>
      </ul>
      <div style="display:flex;gap:8px;">
        <button class="btn btn-primary btn-sm" onclick="_gsGoTo('sp-tags','enrich-run-btn')" style="flex:1;justify-content:center;">Go to Auto-Enrich →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">🎤 Sing</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        The Sing tab is your command center on a karaoke or open-mic night. Build a saved <b style="color:var(--text);">setlist</b> for the evening, hit the <b style="color:var(--text);">randomiser</b> when you can't decide, accept incoming <b style="color:var(--text);">song requests</b> from BAR friends (with a full request log so nothing slips), and pop the <b style="color:var(--text);">recording studio</b> to grab photo + song + venue + performers in one shot.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Every song you sing becomes a <b style="color:var(--text);">performance log entry</b> — editable, taggable, with a self-rating (crushed / solid / off) that feeds the Stats charts. The log is the foundation of every visualization BAR builds about your singing.
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;">
        <button class="btn btn-primary btn-sm" onclick="showTab('night');" style="flex:1;justify-content:center;">Open Sing →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">🎙️ Vox Gym — understand &amp; improve</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Want to understand what your voice is actually doing — and how to make it do more? Vox Gym is a deep-dive into vocal anatomy, technique, and famous voices, plus structured practice routines for foundations, fault correction, and technique building. Tap a technique chip on any song's detail to jump straight to exercises that train it.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Famous Voice Profiles database</b> — 823 artists tagged across foundational technique (register, resonance) and stylistic technique (texture, phrasing). Curated by humans, not scraped — useful for finding songs that train a specific technique, or for understanding what a singer you admire is actually doing with their voice.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Voice notes</b> — tap the 🎤 mic FAB (lower-left, anywhere in BAR) to record a vocal memo. Capture an idea, a phrase you're working on, or a take you want to compare against later.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Reference keyboard</b> — the 🎹 piano FAB (lower-right) opens a built-in pitch-reference keyboard for working out notes or checking your range without needing a separate app.
      </div>
      <div style="display:flex;gap:8px;">
        <button class="btn btn-primary btn-sm" onclick="showTab('voxgym');" style="flex:1;justify-content:center;">Explore Vox Gym →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">📍 Venue Discovery, Management, and Hubs</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Karaoke spots, open-mic rooms, live-band stages — these are the <b style="color:var(--text);">third spaces</b> where singers actually live and grow. BAR helps you find them, track them, and remember what you sang where. Save your regular spots to <em>My Venues</em>, browse community-built directories like DMV's karaoke scene, or ask AI to find new ones near you for whichever night you're up for.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        BAR's first <b style="color:var(--text);">featured region</b> is the DC / Maryland / Virginia area, where 22 of the most-frequented venues have <b style="color:var(--text);">dedicated Venue Hubs</b> with check-in, schedule, your private notes, your performance history there, AI-driven "what should I sing tonight" suggestions, and crowd-tested songs aggregated from friends who've performed there. Living elsewhere? Use <b style="color:var(--text);">My Venues</b> to build your own list and <b style="color:var(--text);">AI Find</b> to discover spots anywhere — more featured regions on the roadmap.
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;">
        <button class="btn btn-primary btn-sm" onclick="showTab('venues');" style="flex:1;justify-content:center;">Open Venues →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">👥 Social, Friends, and Third-Space Connections</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Most "social" apps connect strangers via algorithm and monetize your attention with ads, and sell your data. <b style="color:var(--text);">BAR doesn't do that.</b> BAR is for managing connections with the people you share public spaces with — the karaoke regulars, the open-mic crews, and the friends who already know you sing.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Privacy by design.</b> No public directory. No friend-of-friend graph. No data sold or rented. Friends connect in one of 4 ways — 2 high-trust, immediate connection methods, and 2 lower-trust, delayed connection methods — you choose which approach best fits the situation. By default friends only see your BAR name and avatar emoji. You choose <b style="color:var(--text);">per friend, per feature</b> what they can see: Repertoire, Workshop, individual stat groups, trophies, performance log, ability to send song requests, ability to message you.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Napkin Notes</b> handles the people who aren't on BAR yet — that singer you met at the open-mic and want to remember next week. Local-only, never synced, never shared. Your private memory of the room.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Chat privacy.</b> The Messages tab has a <b style="color:var(--text);">⚙ Settings</b> button at the top of the conversation list. Four privacy toggles, all defaulting to OFF: browser notifications for new messages, sending read receipts (✓✓), sending typing indicators (••• is typing…), and showing message previews in lock-screen push notifications. Each toggle controls what <em>you send out</em>; what you see from friends is up to them.
      </div>
      <div style="display:flex;gap:8px;">
        <button class="btn btn-primary btn-sm" onclick="showTab('people');" style="flex:1;justify-content:center;">Open People →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">📈 See your journey</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Every performance you log builds your story. The Stats tab visualizes your journey with specialized charts — genre map, mastery progress, voice technique coverage, performance trends, venue patterns, momentum, artist reach. The longer you use BAR, the richer the picture.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">Self-rate</b> each performance after you sing it (crushed / solid / off). BAR uses the ratings to surface your top-rated songs AND your "songs to workshop" — the ones you've struggled with and might want to practice. Quiet, private feedback for yourself, not a leaderboard.
      </div>
      <div style="display:flex;gap:8px;">
        <button class="btn btn-primary btn-sm" onclick="showTab('stats');" style="flex:1;justify-content:center;">See My Stats →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">🏆 Trophies &amp; milestones</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        BAR unlocks trophies as you hit milestones and meet other hidden requirements — add your first song, tag "x" number of songs, perform "x" songs by certain artists, perform at certain venues, etc. The more you interact with BAR, the more trophies you'll unlock. <b style="color:var(--text);">Log all your performances to maximize trophy unlocks!</b> They're motivational, not points-grindable; the goal is to reward you for continuing your singing journey.
      </div>
      <div style="display:flex;gap:8px;">
        <button class="btn btn-primary btn-sm" onclick="window._statsLandView='trophy';showTab('stats');" style="flex:1;justify-content:center;">Open Trophy Room →</button>
      </div>
    </div>

    <div class="settings-section">
      <div class="settings-title">💾 Your data stays yours</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        <b style="color:var(--text);">No ads. No tracking pixels. No data sold or resold.</b> BAR's business model is the paid tier — your songbook, performances, and voice journey aren't the product. They're yours.
      </div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Sign in (recommended) and everything syncs to Supabase cloud and across devices automatically. Stay signed out and BAR works entirely on-device — just remember to <b style="color:var(--text);">back up often</b>. Either way, you can export a full backup of your data, export your songbook as a shareable file, or delete your account and wipe everything anytime.
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;">
        <button class="btn btn-primary btn-sm" onclick="showSettingsPage('sp-account',document.querySelector('[onclick*=sp-account]'))" style="flex:1;justify-content:center;">Backup &amp; Restore →</button>
      </div>
    </div>

    <!-- Orientation-tips RESTORE moved to Settings (2026-06-07); link to it here. -->
    <div class="settings-section">
      <div class="settings-title">💡 Orientation tips</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">
        Each main page shows a short welcome card the first time you visit. To bring dismissed cards back, use <b>💡 Restore orientation tips</b> in Settings.
      </div>
      <button class="btn btn-secondary btn-sm" onclick="openSettings()" style="width:100%;justify-content:center;">⚙️ Open Settings</button>
    </div>

  </div>

  <!-- ═══ PAGE: INSTALL AS AN APP (☰ More page, 2026-06-07) ═══════════════════
       Extracted out of the Walkthrough into its own More-hub page. install-pwa-section
       id kept (CRITICAL_ID + _setInstallTab/renderInstallSection refs); shown
       always on this dedicated page (showSettingsPage forces it + the platform tab). -->
  <div id="sp-install" class="settings-page" style="display:none;">
    <div class="settings-section" id="install-pwa-section">
      <div class="settings-title">📲 Install BAR as an App</div>
      <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:12px;">
        Install BAR to your home screen for a full-screen, app-like experience — no browser bar, faster launch, works offline.
      </div>
      <div style="display:flex;gap:6px;margin-bottom:12px;">
        <button id="install-tab-ios" class="tab-sub-btn" onclick="_setInstallTab('ios')" style="flex:1;">🍎 iPhone / iPad</button>
        <button id="install-tab-android" class="tab-sub-btn" onclick="_setInstallTab('android')" style="flex:1;">🤖 Android</button>
      </div>
      <div id="install-ios-block" style="display:none;">
        <div style="font-size:13px;color:var(--text);line-height:1.6;margin-bottom:6px;">
          <b>You must use Safari</b> — Chrome / Firefox / Edge on iOS don't support real PWA install.
        </div>
        <ol style="font-size:13px;color:var(--text2);line-height:1.8;padding-left:22px;margin:6px 0 0;">
          <li>Open this page in <b>Safari</b>.</li>
          <li>Tap the <b>Share</b> button (square with up arrow, bottom of screen).</li>
          <li>Scroll down and tap <b>Add to Home Screen</b>.</li>
          <li>Tap <b>Add</b> in the top-right.</li>
        </ol>
      </div>
      <div id="install-android-block" style="display:none;">
        <div style="font-size:13px;color:var(--text);line-height:1.6;margin-bottom:6px;">
          Best in <b>Chrome</b>. Chrome usually prompts automatically with an "Install app" banner — tap that if you see it.
        </div>
        <ol style="font-size:13px;color:var(--text2);line-height:1.8;padding-left:22px;margin:6px 0 0;">
          <li>Open this page in <b>Chrome</b>.</li>
          <li>Tap the <b>⋮</b> menu (top-right).</li>
          <li>Tap <b>Install app</b> (or <b>Add to Home screen</b>).</li>
          <li>Tap <b>Install</b>.</li>
        </ol>
      </div>
    </div>
  </div>

  <!-- ═══ PAGE: YOUR DATA (☰ More page, 2026-06-07) — backup / diagnose / clear.
       Extracted out of Settings → Account so Settings is genuine config only. -->
  <div id="sp-yourdata" class="settings-page" style="display:none;">
    <div class="settings-section">
      <div class="settings-title">📦 Backup &amp; Restore</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:12px;line-height:1.6;">
        Export a full backup, load a previous backup, or download your songbook as a shareable file.
      </div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;font-size:14px;" onclick="openDataModal()">📂 Open Data Manager ↓↑</button>
    </div>

    <div class="settings-section">
      <div class="settings-title">🔍 Diagnose Data</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:10px;line-height:1.6;">Scans your song data for common issues — missing fields, duplicate entries, or corrupt records.</div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="diagnoseSongData()">🔍 Diagnose Data Problems</button>
      <div id="diag-output" style="font-size:11px;color:var(--text2);line-height:1.8;margin-top:8px;"></div>
    </div>

    <div class="settings-section">
      <div style="font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:1px;font-weight:700;text-align:center;margin-bottom:10px;">Danger Zone</div>
      <button class="btn btn-danger" style="width:100%;justify-content:center;" onclick="clearAllData()">🗑️ Clear All Data</button>
    </div>
  </div>

  <!-- ═══ PAGE: PROFILE (default-shown config page, 2026-06-07) ═══════════════ -->
  <div id="sp-profile" class="settings-page">
    <div id="profile-signed-out" class="settings-section" style="display:none;">
      <div class="empty-state" style="padding:18px 8px;">
        <div class="empty-icon">🔒</div>
        <h3>Sign in to manage your profile</h3>
        <p>Profile settings require a BAR account.</p>
        <button class="btn btn-primary" style="margin-top:10px;" onclick="handleAuthHeaderBtn()">☁️ Sign In / Sign Up</button>
      </div>
    </div>
    <div id="profile-signed-in" style="display:none;">
      <div class="settings-section">
        <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
          <div class="settings-title" style="margin-bottom:0;">🎤 Your Profile</div>
          <button class="btn btn-secondary btn-sm" style="font-size:12px;" onclick="openProfileModal(kkUser?.id)">👁 Preview</button>
        </div>
        <!-- Avatar / emoji picker -->
        <div style="display:flex;flex-direction:column;align-items:center;margin-bottom:18px;">
          <div id="acct-avatar" onclick="toggleEmojiPicker()" title="Tap to pick an icon"
            style="width:72px;height:72px;border-radius:50%;background:rgba(124,58,237,0.18);border:2px solid rgba(124,58,237,0.4);display:flex;align-items:center;justify-content:center;font-size:34px;cursor:pointer;user-select:none;">👤</div>
          <div style="font-size:11px;color:var(--text3);margin-top:6px;">Tap to change icon</div>
          <div id="emoji-picker-panel" style="display:none;margin-top:10px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius);padding:10px;width:100%;box-sizing:border-box;">
            <div style="display:grid;grid-template-columns:repeat(8,1fr);gap:4px;text-align:center;">
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎤')">🎤</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎸')">🎸</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎹')">🎹</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎺')">🎺</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎻')">🎻</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🥁')">🥁</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎵')">🎵</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎶')">🎶</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎭')">🎭</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🌟')">🌟</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('⭐')">⭐</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🔥')">🔥</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('💫')">💫</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('✨')">✨</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('👑')">👑</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🏆')">🏆</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🦋')">🦋</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🌈')">🌈</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎯')">🎯</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('💎')">💎</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🦁')">🦁</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🐯')">🐯</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🌺')">🌺</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🌙')">🌙</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🤘')">🤘</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('❤️')">❤️</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('💜')">💜</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🖤')">🖤</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎉')">🎉</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎊')">🎊</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🎓')">🎓</span>
              <span style="font-size:24px;cursor:pointer;padding:4px;border-radius:6px;" onclick="selectProfileEmoji('🍀')">🍀</span>
            </div>
          </div>
        </div>
        <!-- Fields -->
        <div style="font-size:11px;color:var(--text3);margin-bottom:14px;line-height:1.5;">Fields marked <span style="color:var(--accent3);font-weight:600;">Share publicly</span> are visible to your BAR friends. Everything else is private to you.</div>

        <div class="form-group" style="margin-bottom:12px;">
          <label class="form-label">🎤 BAR Name <span style="font-size:10px;color:var(--text3);font-weight:400;">— always shown</span></label>
          <input class="form-input" id="acct-bar-name" placeholder="Your unique BAR name">
        </div>

        <div class="form-group" style="margin-bottom:12px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
            <label class="form-label" style="margin:0;">⭐ Signature Song</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-sig-song" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <input class="form-input" id="acct-sig-song" placeholder="Your go-to performance">
        </div>

        <div class="form-group" style="margin-bottom:12px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
            <label class="form-label" style="margin:0;">🔥 Currently Obsessed With</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-obsession" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <input class="form-input" id="acct-obsession" placeholder="What you can't stop singing right now">
        </div>

        <div class="form-group" style="margin-bottom:12px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
            <label class="form-label" style="margin:0;">🎤 Favorite Singers</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-fav-singers" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <input class="form-input" id="acct-fav-singers" placeholder="e.g. Whitney Houston, Freddie Mercury">
        </div>

        <div class="form-group" style="margin-bottom:12px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
            <label class="form-label" style="margin:0;">🎵 Favorite Genres</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-fav-genres" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <div id="prof-genre-chips" style="display:flex;flex-wrap:wrap;gap:6px;"></div>
        </div>

        <div class="form-group" style="margin-bottom:12px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
            <label class="form-label" style="margin:0;">🎙️ Vocal Styles</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-vocal-styles" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <div id="prof-vocal-chips" style="display:flex;flex-wrap:wrap;gap:6px;"></div>
        </div>

        <div class="form-group" style="margin-bottom:12px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
            <label class="form-label" style="margin:0;">📍 City / Area</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-city" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <input class="form-input" id="acct-city" placeholder="Where you perform">
          <div style="font-size:11px;color:var(--text3);margin-top:4px;line-height:1.45;">Used to scope <b style="color:var(--text2);">Browse</b> and <b style="color:var(--text2);">AI Find</b> to your region. Currently the only featured region is DC / Maryland / Virginia — more on the roadmap.</div>
        </div>

        <div class="form-group" style="margin-bottom:16px;">
          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
            <label class="form-label" style="margin:0;">💬 Bio</label>
            <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);cursor:pointer;white-space:nowrap;">
              <input type="checkbox" id="prof-show-bio" style="accent-color:var(--accent3);">
              Share publicly
            </label>
          </div>
          <textarea class="form-textarea" id="acct-bio" placeholder="One line about you as a performer…" maxlength="120" style="min-height:70px;resize:none;"></textarea>
          <div style="font-size:11px;color:var(--text3);margin-top:4px;">120 chars max</div>
        </div>

        <div id="acct-email" style="font-size:12px;color:var(--text3);word-break:break-all;margin-bottom:10px;"></div>
        <button class="btn btn-primary" style="width:100%;justify-content:center;" onclick="acctSaveProfile()">Save Profile</button>
      </div>
    </div>
  </div>

  <!-- ═══ PAGE: ACCOUNT ═════════════════════════════════════════════════════ -->
  <div id="sp-account" class="settings-page" style="display:none;">
    <div id="account-signed-out" class="settings-section" style="display:none;">
      <div class="empty-state" style="padding:18px 8px;">
        <div class="empty-icon">🔒</div>
        <h3>Sign in to manage your account</h3>
        <p>Notification and security settings live here once you sign in.</p>
        <button class="btn btn-primary" style="margin-top:10px;" onclick="handleAuthHeaderBtn()">☁️ Sign In / Sign Up</button>
      </div>
    </div>
    <div id="account-signed-in" style="display:none;">

      <div class="settings-section">
        <div class="settings-title">🔔 Message Notifications</div>
        <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 0;">
          <div style="flex:1;padding-right:12px;">
            <div style="font-size:13px;font-weight:600;color:var(--text);">Browser notifications for new messages</div>
            <div style="font-size:12px;color:var(--text3);margin-top:2px;line-height:1.5;">Pops a desktop alert when a friend messages you while BAR is in another tab. Works on desktop browsers; on mobile, install BAR to your home screen first.</div>
          </div>
          <label style="display:flex;align-items:center;gap:8px;cursor:pointer;flex-shrink:0;" onclick="toggleMsgNotifs()">
            <div id="acct-msgnotif-track" class="ios-toggle-track" style="cursor:pointer;"></div>
          </label>
        </div>
        <div id="acct-msgnotif-status" style="font-size:11px;color:var(--text3);margin-top:4px;line-height:1.5;"></div>
      </div>

      <div class="settings-section">
        <div class="settings-title">🏆 Achievements</div>
        <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 0;">
          <div>
            <div style="font-size:13px;font-weight:600;color:var(--text);">Trophy pop-ups</div>
            <div style="font-size:12px;color:var(--text3);margin-top:2px;">Show a banner when you unlock a new achievement</div>
          </div>
          <label style="display:flex;align-items:center;gap:8px;cursor:pointer;" onclick="toggleTrophyNotifs()">
            <div id="settings-notif-track" class="ios-toggle-track" style="cursor:pointer;"></div>
          </label>
        </div>
      </div>

      <!-- 2026-06-06 — Connect Code retired. It only ever powered the Instant-Connect
           (BAR name + code) friend path, which was removed in the friend-connect
           simplification. Adding a friend is now: have them search your BAR name. -->

      <div class="settings-section">
        <div class="settings-title">🔑 Security</div>
        <button class="btn btn-secondary" style="width:100%;justify-content:center;margin-bottom:10px;" onclick="acctOpenChangePassword()">🔑 Change Password</button>
        <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="authSignOut()">Sign Out</button>
      </div>

      <!-- 📸 SCREENSHOT MODE — owner-only (revealed by renderAccountPage when _usePersonalKey is set). Hides displayed email/PII in the UI for marketing captures. -->
      <div class="settings-section" id="acct-screenshot-section" style="display:none;">
        <div class="settings-title">📸 Screenshot Mode</div>
        <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 0;">
          <div style="flex:1;padding-right:12px;">
            <div style="font-size:13px;font-weight:600;color:var(--text);">Hide email &amp; BAR name in UI</div>
            <div style="font-size:12px;color:var(--text3);margin-top:2px;line-height:1.5;">For taking clean marketing / App Store screenshots without your real identity showing in the header, account view, or profile preview. Swaps email for <em><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3b5f5e56547b594e52575f52555c5a49545850484f5a4915585456">[email&#160;protected]</a></em> and your own BAR name for <em>DemoSinger</em>. Friend BAR names are unaffected (those aren't yours to mask). Auth and sync are unaffected. Auto-expires after 24 hours so you can't leave it on by accident.</div>
          </div>
          <label style="display:flex;align-items:center;gap:8px;cursor:pointer;flex-shrink:0;" onclick="toggleScreenshotMode()">
            <div id="acct-screenshot-track" class="ios-toggle-track" style="cursor:pointer;"></div>
          </label>
        </div>
      </div>


      <!-- ─── Orientation tips (moved here from the Walkthrough, 2026-06-07).
           The data TOOLS (Backup/Restore · Diagnose · Clear All Data) moved to
           ☰ More → Your Data; Delete My Account stays here as account management. -->
      <div class="settings-section">
        <div class="settings-title">💡 Show orientation tips</div>
        <div style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:12px;">
          Each main page shows a short, dismissible welcome card the first time you visit. If you've dismissed them and want them back, tap below — all hidden cards return immediately.
        </div>
        <button class="btn btn-primary btn-sm" onclick="restoreAllOrientationCards()" style="width:100%;justify-content:center;">💡 Restore orientation tips</button>
      </div>

      <!-- ─── DANGER ZONE (account management; data tools live in More → Your Data) ── -->
      <div class="settings-section">
        <div style="font-size:10px;color:var(--text3);text-transform:uppercase;letter-spacing:1px;font-weight:700;text-align:center;margin-bottom:10px;">Danger Zone</div>
        <button class="btn" style="width:100%;justify-content:center;background:rgba(232,57,90,0.08);border:1px solid rgba(232,57,90,0.4);color:var(--accent);font-family:'DM Sans',sans-serif;" onclick="acctOpenDelete()">🗑 Delete My Account</button>
      </div>
    </div>
  </div>

  <!-- ═══ PAGE: SONG TAGGING ════════════════════════════════════════════════ -->
  <div id="sp-tags" class="settings-page" style="display:none;">

    <div class="settings-section">
      <div class="settings-title">🏷️ Custom Tags</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:10px;line-height:1.6;">
        Create your own tags to appear across all filter lists. They show in teal to distinguish them from Performance and Genre tags.
      </div>
      <div style="display:flex;gap:8px;margin-bottom:10px;">
        <input class="form-input" id="new-custom-tag-input" placeholder="e.g. Beach Songs" style="flex:1;padding:7px 10px;font-size:13px;" onkeydown="if(event.key==='Enter')addCustomTag()">
        <button class="btn btn-primary btn-sm" onclick="addCustomTag()">＋ Add</button>
      </div>
      <div id="custom-tags-list" style="display:flex;flex-wrap:wrap;gap:6px;"></div>
    </div>

    <!-- ─── UNIFIED AUTO-TAG & ENRICH ──────────────────────────── -->
    <div class="settings-section">
      <div class="settings-title">⚡ Auto-Enrich Songbook</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:12px;line-height:1.6;">
        <b style="color:var(--text);">New songs auto-enrich at add time</b> — this button is the catch-up for older ones.
        It fills in missing metadata (artwork, year, genre, album, duration, key, BPM),
        AI <b style="color:var(--text);">performance + theme tags</b>, and KaraFun availability (if a catalog is loaded below).
      </div>

      <!-- Backlog status — informational, no checkboxes -->
      <div style="background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:8px;font-size:12px;color:var(--text2);line-height:1.7;">
        <div>📀 iTunes — <span id="enrich-itunes-count" style="color:var(--text3);"></span></div>
        <div>🏷️ AI Performance Tags — <span id="enrich-perf-count" style="color:var(--text3);"></span></div>
        <div>🎭 AI Themes — <span id="enrich-theme-count" style="color:var(--text3);"></span></div>
        <div>🎤 KaraFun Availability — <span id="enrich-kf-count" style="color:var(--text3);"></span></div>
      </div>

      <!-- AI Vocal Data paid-locked placeholder (kept as preview of upcoming feature) -->
      <div style="background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:8px;opacity:0.55;pointer-events:none;user-select:none;font-size:12px;color:var(--text2);line-height:1.7;">
        <div style="display:flex;align-items:center;gap:8px;margin-bottom:2px;">
          <span style="font-size:13px;font-weight:600;color:var(--text);">🎤 AI Vocal Data</span>
          <span style="font-size:10px;font-weight:700;color:#fff;background:#7c3aed;border-radius:4px;padding:1px 6px;letter-spacing:0.5px;">🔒 PAID</span>
        </div>
        <div style="font-size:11px;color:var(--text3);">Song-specific AI analysis — vocal note range (low, high, tessitura) and voice technique tags. <span id="enrich-range-count"></span></div>
      </div>

      <div id="itunes-enrich-status" style="display:none;"></div>
      <div id="vocal-tag-coverage" style="display:none;"></div>
      <div style="font-size:11px;color:var(--text3);margin-bottom:10px;padding:8px 10px;background:var(--surface2);border-radius:var(--radius-sm);line-height:1.5;">Fills in missing iTunes + AI tag data only — won't overwrite existing values. <strong style="color:var(--text2);">Exception:</strong> the KaraFun check re-audits every song (songs come and go from KaraFun's catalog over time, so the KaraFun ✓ flag is refreshed against your latest uploaded catalog). To redo a single song from scratch, use ✨ Auto-Enrich + ↻ Force refresh in the song's Edit modal.</div>
      <button class="btn btn-primary" style="width:100%;justify-content:center;" id="enrich-run-btn" onclick="runEnrichmentPipeline()">▶ Auto-Enrich Songbook</button>
      <div id="enrich-pipeline-status" style="font-size:12px;color:var(--text2);margin-top:8px;line-height:1.6;min-height:18px;"></div>
    </div>

    <!-- ─── Refresh Tags From Catalog (item 11 Phase 1 — rubric-freshness) ── -->
    <div class="settings-section">
      <div class="settings-title">🔄 Refresh Tags From Catalog</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:12px;line-height:1.6;">
        BAR's shared song catalog keeps improving — we sharpen the AI tagging rubric and re-tag songs over time. This pulls the <b style="color:var(--text);">latest performance + theme tags</b> for songs you already have, so your tags stay current. Unlike Auto-Enrich (which only fills blanks), this <em>updates</em> existing AI tags — but <b style="color:var(--text);">your own edits always win</b>: tags you added or removed are never touched.
      </div>
      <button class="btn btn-primary" style="width:100%;justify-content:center;" id="catalog-refresh-btn" onclick="_refreshTagsUI()">🔄 Refresh tags from catalog</button>
      <div id="catalog-refresh-status" style="font-size:12px;color:var(--text2);margin-top:8px;line-height:1.6;min-height:18px;"></div>
    </div>

    <!-- ─── KaraFun Catalog ──────────────────────────────────────── -->
    <div class="settings-section">
      <div class="settings-title">📄 KaraFun Catalog</div>
      <div id="kf-freshness-banner" style="margin-bottom:10px;"></div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:8px;line-height:1.6;">
        Required for KaraFun Availability above. Download the CSV monthly — KaraFun adds ~400 songs/month.
      </div>
      <div style="background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:10px;font-size:12px;color:var(--text3);line-height:1.7;">
        1. Tap <a href="https://www.karafun.com/karaoke-song-list.html" target="_blank" style="color:var(--accent);">Get KaraFun Catalog ↗</a> below &nbsp;
        2. Click <strong style="color:var(--text);">Available in CSV format</strong> (not the PDF) &nbsp;
        3. Load the file here — no account needed
      </div>
      <a href="https://www.karafun.com/karaoke-song-list.html" target="_blank" class="btn btn-secondary" style="width:100%;justify-content:center;margin-bottom:10px;font-size:13px;">📄 Get KaraFun Catalog ↗</a>
      <div style="display:flex;gap:8px;margin-bottom:6px;align-items:center;">
        <label for="kf-csv-file" class="btn btn-primary btn-sm" style="cursor:pointer;">📄 Load New CSV</label>
        <input type="file" id="kf-csv-file" accept=".csv,text/csv" style="display:none" onchange="loadKaraFunCSV(event)">
        <button class="btn btn-secondary btn-sm" onclick="clearKaraFunCatalog()" style="color:var(--text3);">✕ Clear</button>
        <div id="kf-csv-loaded" style="font-size:12px;color:var(--text3);flex:1;"></div>
      </div>
      <div id="kf-last-checked" style="font-size:11px;color:var(--text3);margin-top:4px;"></div>
      <div id="kf-check-status" style="font-size:12px;color:var(--text2);margin-top:4px;line-height:1.6;"></div>
    </div>

    <!-- ─── Clear Tag Data ───────────────────────────────────────── -->
    <!-- Removed Clear AI Vocal Data + Clear All Vocal Tags (paid-locked
         feature + FVP tags now derived live at render time; only a
         handful of legacy users had data in those fields). Kept iTunes
         + AI Performance Tags clears as legitimate power-user tools for
         "iTunes picked wrong match" / "rubric updated, retag fresh"
         scenarios. -->
    <div class="settings-section">
      <div class="settings-title">🗑️ Clear Tag Data</div>
      <div style="display:flex;flex-direction:column;gap:8px;">
        <button class="btn btn-secondary" style="width:100%;justify-content:center;font-size:12px;color:var(--text3);" onclick="clearItunesData()">🗑️ Clear iTunes Data</button>
        <button class="btn btn-secondary" style="width:100%;justify-content:center;font-size:12px;color:var(--text3);" onclick="clearAllBulkTags()">🗑️ Clear AI Performance Tags</button>
      </div>
      <div id="clear-bulk-tag-status" style="font-size:12px;color:var(--text2);margin-top:6px;"></div>
      <div id="clear-vocal-status" style="font-size:12px;color:var(--text2);margin-top:4px;"></div>
    </div>

    <div class="settings-section">
      <div class="settings-title">🧹 Clean Up Stale Tags</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:12px;line-height:1.6;">
        Removes tag strings from your songbook that no longer exist in any tag list — performance tags, genre tags, era tags, or your saved custom tags. Useful after renaming or deleting a custom tag.
      </div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="cleanUpStaleTags()">🧹 Clean Up Stale Tags</button>
      <div id="stale-tag-status" style="font-size:12px;color:var(--text2);margin-top:8px;"></div>
    </div>

    <!-- ─── 🤖 AI Tagging Rubric (owner-only, EDIT_MODE-gated) ──────────
         The 12 perf-tag definitions used by the AI tagger and the Help
         modal Filter Guide. Edits here update perf_tag_rubric on
         Supabase via the perf-tag-rubric-edit Edge Function and flow
         to all users' AI tagging on their next load. Display toggled
         on by admin.js resolveEditMode() — hidden in production for
         non-owner users. -->
    <div id="perf-tag-rubric-admin-section" class="settings-section" style="display:none;border:1px solid rgba(124,58,237,0.3);background:rgba(124,58,237,0.04);">
      <div class="settings-title">🤖 AI Tagging Rubric <span style="font-size:10px;font-weight:700;color:#fff;background:#7c3aed;border-radius:4px;padding:1px 6px;letter-spacing:0.5px;margin-left:6px;">OWNER</span></div>
      <div style="font-size:12px;color:var(--text2);margin-bottom:10px;line-height:1.6;">
        Live-tunable definitions for the 12 AI-tagged performance tags. The AI tagger injects these into every Gemini call as a rubric; the Help modal Filter Guide also reads from them. Edits propagate to ALL users on their next page load. Bundled fallback in <code>technique_guide.js</code> still ships for offline / cache-miss.
      </div>
      <div id="perf-tag-rubric-admin-list"></div>
      <div style="font-size:11px;color:var(--text3);margin-top:8px;line-height:1.5;">↺ Revert resets the textarea to the cached value; only 💾 Save persists to the cloud.</div>
    </div>
  </div>

  <!-- ═══ PAGE: REPORT A BUG ═════════════════════════════════════════════════ -->
  <div id="sp-bug" class="settings-page" style="display:none;">
    <div class="settings-section">
      <div class="settings-title">🐛 Report a Bug</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:12px;line-height:1.6;">
        Something not working? Fill in what you can and tap Send — it'll open your email app with everything pre-filled.
      </div>
      <div style="display:flex;flex-direction:column;gap:10px;">
        <div>
          <label style="font-size:12px;font-weight:600;color:var(--text2);display:block;margin-bottom:4px;">What happened?</label>
          <textarea id="bug-description" class="form-textarea" placeholder="Describe what you were doing and what went wrong…" style="min-height:100px;resize:vertical;"></textarea>
        </div>
        <div style="display:flex;gap:8px;">
          <div style="flex:1;">
            <label style="font-size:12px;font-weight:600;color:var(--text2);display:block;margin-bottom:4px;">Device / OS</label>
            <select id="bug-os" class="form-select" style="width:100%;">
              <option value="">Select…</option>
              <option>iPhone</option>
              <option>iPad</option>
              <option>Android phone</option>
              <option>Android tablet</option>
              <option>Mac</option>
              <option>Windows PC</option>
              <option>Linux</option>
              <option>Other</option>
            </select>
          </div>
          <div style="flex:1;">
            <label style="font-size:12px;font-weight:600;color:var(--text2);display:block;margin-bottom:4px;">Browser</label>
            <select id="bug-browser" class="form-select" style="width:100%;">
              <option value="">Select…</option>
              <option>Safari</option>
              <option>Chrome</option>
              <option>Firefox</option>
              <option>Edge</option>
              <option>Samsung Internet</option>
              <option>Other</option>
            </select>
          </div>
        </div>
        <div>
          <label style="font-size:12px;font-weight:600;color:var(--text2);display:block;margin-bottom:4px;">Screenshot (optional)</label>
          <div style="display:flex;gap:8px;align-items:center;">
            <button class="btn btn-secondary btn-sm" onclick="document.getElementById('bug-screenshot').click()">📷 Attach screenshot</button>
            <input type="file" id="bug-screenshot" accept="image/*" style="display:none" onchange="onBugScreenshot(event)">
            <span id="bug-screenshot-name" style="font-size:12px;color:var(--text3);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></span>
          </div>
          <div id="bug-screenshot-preview" style="margin-top:8px;display:none;">
            <img id="bug-screenshot-img" style="max-width:100%;max-height:160px;border-radius:8px;border:1px solid var(--border);">
          </div>
        </div>
        <button class="btn btn-primary" style="width:100%;justify-content:center;" onclick="sendBugReport()">📧 Send Bug Report</button>
        <div id="bug-report-status" style="font-size:12px;color:var(--text2);text-align:center;"></div>
      </div>
    </div>
  </div>

  <!-- ═══ PAGE: ABOUT ════════════════════════════════════════════════════════ -->
  <div id="sp-about" class="settings-page" style="display:none;">
    <div class="settings-section">
      <div class="settings-title">ℹ️ About</div>
      <div style="text-align:center;padding:16px 0 8px;">
        <img src="images/bar-logo.png" alt="BAR logo" style="width:160px;height:160px;object-fit:contain;display:block;margin:0 auto 12px;border-radius:24px;">
        <div style="font-size:22px;font-weight:800;background:linear-gradient(135deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:6px;">Building A Rockstar v4.99885</div>
        <div style="font-size:14px;color:var(--text2);margin-bottom:6px;">Your pocket sherpa from seat to stage.</div>
        <div style="font-size:12px;color:var(--text3);">Vibe-coded with love by Miiiiiiike N. / U-Street</div>
      </div>
    </div>
    <div class="settings-section">
      <div style="font-size:13px;color:var(--text2);line-height:1.8;">
        <p style="margin:0 0 12px;">Building A Rockstar is for anyone who sings — whether you're a karaoke regular with a trusty go-to list, an open-mic singer building a real set, or someone trying to take their voice further. If you sing, this is for you.</p>
        <p style="margin:0 0 12px;">The free tier handles the busywork of being a singer: a smart, taggable songbook; setlists you can build and reorder for tonight's venue; a performance log that remembers what you sang where; AI that finds new songs you'll enjoy; and crowd-tested picks from your favorite spots. No account required.</p>
        <p style="margin:0 0 12px;">For singers who want to keep growing, BAR believes <em>the stage is your training ground</em>. Karaoke and open-mic nights give you a real audience, real nerves, and genuine feedback — and every performance you log becomes data you can learn from. The paid tier goes deeper with personalized voice analysis and exercises matched to your actual voice, but it's there when you want it, not in the way when you don't.</p>
        <p style="margin:0;font-weight:700;color:var(--text);">Go ahead. Build A Rockstar.</p>
      </div>
    </div>
    <div class="settings-section">
      <div class="settings-title">🔄 Updates</div>
      <div style="font-size:13px;color:var(--text2);margin-bottom:12px;line-height:1.6;">
        You're running <strong>v4.99885</strong>. If Mike has pushed an update, tapping below will reload the app with the latest version. Your song data won't be affected.
      </div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="checkForUpdate()">🔄 Reload to get latest version</button>
      <div id="update-status" style="font-size:12px;color:var(--text3);text-align:center;margin-top:8px;"></div>
    </div>
    <div class="settings-section">
      <div class="settings-title">🎼 Data Sources</div>
      <div style="font-size:12px;color:var(--text2);line-height:1.7;">
        Song key &amp; BPM data via <a href="https://getsongbpm.com" target="_blank" rel="noopener" style="color:var(--accent2);font-weight:600;text-decoration:underline;">getsongbpm.com</a>.
        Release year &amp; genre data via <a href="https://musicbrainz.org" target="_blank" rel="noopener" style="color:var(--accent2);font-weight:600;text-decoration:underline;">MusicBrainz</a>, <a href="https://www.wikidata.org" target="_blank" rel="noopener" style="color:var(--accent2);font-weight:600;text-decoration:underline;">Wikidata</a>, and <a href="https://www.discogs.com" target="_blank" rel="noopener" style="color:var(--accent2);font-weight:600;text-decoration:underline;">Discogs</a> (all CC0 / freely-licensed).
      </div>
    </div>
    <div class="settings-section" style="text-align:center;">
      <button class="btn btn-secondary btn-sm" onclick="openLegalModal('tos')" style="font-size:12px;color:var(--text3);">⚖️ Legal — Terms &amp; Privacy</button>
    </div>
  </div>

  </div>


</main>

<!-- TOAST -->
<div class="toast" id="toast"></div>

<!-- LEGAL MODAL -->
<div class="modal-overlay" id="legal-modal" onclick="if(event.target===this)closeModal('legal-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div style="margin-bottom:14px;">
        <div class="modal-title" style="margin-bottom:0;">⚖️ Legal</div>
      </div>
      <div style="display:flex;gap:8px;margin-bottom:16px;">
        <button id="legal-tab-tos" class="tab-sub-btn active" style="flex:1;" onclick="switchLegalTab('tos')">Terms of Service</button>
        <button id="legal-tab-pp" class="tab-sub-btn" style="flex:1;" onclick="switchLegalTab('pp')">Privacy Policy</button>
      </div>

      <!-- TERMS OF SERVICE -->
      <div id="legal-tos" style="font-size:12px;color:var(--text2);line-height:1.75;">
        <p style="color:var(--text3);margin:0 0 12px;"><em>Effective May 24, 2026</em></p>
        <p style="margin:0 0 10px;">By using Building A Rockstar ("BAR", "the app", available at buildingarockstar.com and buildingarockstar.app), you agree to these Terms. If you do not agree, do not use the app.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">1. Eligibility</p>
        <p style="margin:0 0 10px;">You must be at least 13 years old to create an account. By registering, you confirm you meet this requirement.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">2. Your Account</p>
        <p style="margin:0 0 10px;">You are responsible for keeping your credentials secure and for all activity under your account. Your BAR Name must be at least 6 characters and may not impersonate another person. We reserve the right to reclaim names that violate these Terms. We also reserve the right to suspend or terminate accounts or restrict access to the app at our sole discretion, without notice, for conduct that violates these Terms or that we reasonably believe may harm other users or the service.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">3. Acceptable Use</p>
        <p style="margin:0 0 6px;">You agree not to:</p>
        <ul style="margin:0 0 10px;padding-left:18px;">
          <li>Use the app for any unlawful purpose</li>
          <li>Harass, threaten, or harm other users</li>
          <li>Attempt to access other users' accounts or data</li>
          <li>Reverse-engineer, scrape, extract, or programmatically harvest our proprietary databases, curated song catalogs, Famous Voice Profiles, difficulty taxonomies, or underlying application logic for use in competing products or external datasets</li>
        </ul>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">4. Your Content &amp; Social Interactions</p>
        <p style="margin:0 0 10px;">Song lists, performance logs, venue notes, and messages you create remain yours. You grant us a worldwide, non-exclusive, royalty-free license to store, reproduce, host, display, and distribute that data — including through trusted third-party processors necessary to deliver the service (see Section 5) — solely for the purpose of operating, syncing, and delivering the service to you and your designated social connections (e.g., sharing performance cards or sending peer song requests). We do not sell your content.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">5. Third-Party Services</p>
        <p style="margin:0 0 10px;">BAR relies on third-party services — including Supabase (data storage and authentication), Apple iTunes Search API (song metadata), Google Gemini API (AI features), AudD (audio recognition), Cloudflare (hosting), and Resend (email delivery) — to operate. The full current list, and what data is shared with each, is described in the Privacy Policy. Your use of those integrations is also subject to their respective terms.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">6. Disclaimer of Warranties</p>
        <p style="margin:0 0 10px;">BAR is provided "as is" and "as available" without warranty of any kind. We do not guarantee uninterrupted uptime, the preservation or permanence of your historical data (including performance logs, songbook entries, or analytics), or the accuracy of automated AI-generated content (including song tags, voice taxonomy metrics, search suggestions, recommendations, and explanatory text). AI-generated content is not human-reviewed and may be incorrect, inappropriate, or unsuitable for your purposes. You are responsible for any manual backups of data important to you.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">7. Limitation of Liability</p>
        <p style="margin:0 0 10px;">To the maximum extent permitted by law, Building A Rockstar and its creator shall not be liable for any indirect, incidental, special, or consequential damages (including, but not limited to, loss of data, performance history, or community metrics) arising from your use of the app.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">8. Service Modifications</p>
        <p style="margin:0 0 10px;">We may add, modify, suspend, or discontinue any feature of BAR (including paid-tier features) at any time, with or without notice. We are not liable for any consequences of feature changes or service discontinuation, except as required by law.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">9. Changes</p>
        <p style="margin:0 0 10px;">We may update these Terms at any time. Continued use after changes are posted constitutes acceptance.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">10. Governing Law</p>
        <p style="margin:0 0 10px;">These Terms are governed by the laws of the Commonwealth of Virginia, without regard to conflict of law principles. Any legal actions or proceedings related to BAR must be brought exclusively in the courts located in the Commonwealth of Virginia.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">11. Contact</p>
        <p style="margin:0;">Questions? Reach us via the 🐛 Report page in Settings.</p>
      </div>

      <!-- PRIVACY POLICY -->
      <div id="legal-pp" style="display:none;font-size:12px;color:var(--text2);line-height:1.75;">
        <p style="color:var(--text3);margin:0 0 12px;"><em>Effective May 24, 2026</em></p>
        <p style="margin:0 0 10px;">This Privacy Policy explains what data Building A Rockstar ("BAR") collects, how we use it, and your rights.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">1. What We Collect</p>
        <ul style="margin:0 0 10px;padding-left:18px;">
          <li><strong style="color:var(--text);">Account data</strong> — email address and hashed password (managed by Supabase Auth)</li>
          <li><strong style="color:var(--text);">Profile data</strong> — BAR Name, avatar emoji, city, bio, signature song (optional; you set these). Your BAR Name is visible to your BAR friends and searchable by any BAR user who already knows it (e.g. from a verbal mention or a QR share) — that's how name-based friend requests work. The other profile fields are private by default and you opt in per-field.</li>
          <li><strong style="color:var(--text);">App data</strong> — songs, performance logs, venue notes, setlists, and settings you create</li>
          <li><strong style="color:var(--text);">Vocal memos</strong> — audio recordings you make with the in-app mic. Stored in Supabase Storage so they sync across your devices. Visible only to you.</li>
          <li><strong style="color:var(--text);">Social data</strong> — friend connections and direct messages you send within the app</li>
          <li><strong style="color:var(--text);">Push subscriptions</strong> — device push endpoint if you enable notifications (stored to deliver messages)</li>
          <li><strong style="color:var(--text);">Local storage &amp; essential cookies</strong> — We do not use tracking or advertising cookies. We use essential client-side storage (localStorage, sessionStorage, IndexedDB, and the auth session token managed by Supabase Auth) to keep you logged in across sessions, remember your current app state, and store your songbook locally so the app works offline.</li>
        </ul>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">2. How We Use It</p>
        <ul style="margin:0 0 10px;padding-left:18px;">
          <li>To provide, sync, and back up your data across devices</li>
          <li>To enable social features (friends, messaging, song requests)</li>
          <li>To deliver push notifications you opt into</li>
          <li>To compute aggregated counts (e.g. "Crowd-Hot songs at this venue") from friends who have opted into sharing their performance log. Aggregates show counts only — never individual friend identities.</li>
          <li>We do <strong style="color:var(--text);">not</strong> sell your data or use it for advertising</li>
        </ul>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">3. Third-Party Processors</p>
        <ul style="margin:0 0 10px;padding-left:18px;">
          <li><strong style="color:var(--text);">Supabase</strong> (supabase.com) — stores all account, app, and vocal-memo data on US-based servers. Subject to Supabase's privacy policy.</li>
          <li><strong style="color:var(--text);">Cloudflare</strong> (cloudflare.com) — hosts BAR's static files and proxies all HTTP traffic through their edge network. Subject to Cloudflare's privacy policy.</li>
          <li><strong style="color:var(--text);">Google Gemini API</strong> (via our Cloudflare Worker) — when you use AI features (AI Search, Auto-Tag &amp; Enrich, AI Venue Find, or the AI-assisted bug report), we send song titles, artist names, the prompt text you type, and — for bug reports — your report content and any screenshot you attach. No account credentials, passwords, or direct-message contents are transmitted. The API key is stored server-side. Data is processed under Google's Gemini API terms.</li>
          <li><strong style="color:var(--text);">AudD</strong> (audd.io) — when you use the Song/ID FAB (the cyan microphone button), we send a short audio recording (~10 seconds of ambient sound captured by your device's mic) to AudD's music-recognition API. AudD returns the song title and artist if recognized. No account credentials are transmitted. Audio is processed under AudD's privacy policy.</li>
          <li><strong style="color:var(--text);">Resend</strong> (resend.com) — delivers Supabase Auth emails (sign-up confirmation, password reset). Receives your email address only.</li>
          <li><strong style="color:var(--text);">Apple iTunes Search API</strong> — we send song title and artist to retrieve genre, artwork, and release year. No account data is transmitted.</li>
          <li><strong style="color:var(--text);">goQR.me / qrserver.com</strong> — encodes URLs into QR images for the friend-connect flow. No personal data is included in the URL.</li>
        </ul>
        <p style="margin:0 0 10px;"><strong style="color:var(--text);">International data transfers:</strong> Several of our processors (notably Supabase, Google, and Cloudflare) operate from servers based in the United States. If you access BAR from outside the United States — including from the EU, UK, or other jurisdictions with cross-border data transfer restrictions — your data will be transferred to and processed in the US. We rely on our processors' Standard Contractual Clauses (SCCs) and equivalent safeguards to maintain GDPR-equivalent protections.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">4. Data Retention &amp; Deletion</p>
        <p style="margin:0 0 10px;">Your data is kept for as long as your account is active. You can delete your account at any time from Settings → 🔒 Account → Delete Account, which permanently removes all stored data from our backend servers. Note that client-side cached data stored locally on your device (in browser localStorage, sessionStorage, IndexedDB, and similar storage) may persist until you manually clear your browser data or uninstall the app from your home screen.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">5. Security</p>
        <p style="margin:0 0 10px;">We protect your data using industry-standard measures including HTTPS encryption for all data in transit, encryption at rest for data stored on Supabase's infrastructure, password hashing handled by Supabase Auth, and Row-Level Security policies that restrict each user's data access to their own account. We rely on our third-party processors (see Section 3) for additional infrastructure-level protections. No system is perfectly secure, but we take reasonable steps to safeguard your data.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">6. Children</p>
        <p style="margin:0 0 10px;">BAR is not directed at children under 13 (or the applicable legal age of digital consent in your jurisdiction — up to 16 in some EU/UK countries). We do not knowingly collect data from anyone under this age. If you believe a child has provided us data, contact us via the 🐛 Report page and we will immediately delete it.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">7. Your Rights</p>
        <p style="margin:0 0 10px;">You may access, correct, or delete your data at any time directly through the app. Depending on where you live, you may also have additional rights under applicable privacy laws (including the EU/UK GDPR and the California Consumer Privacy Act):</p>
        <ul style="margin:0 0 10px;padding-left:18px;">
          <li>Right of access — request a copy of the personal data we hold about you</li>
          <li>Right to rectification — request that we correct inaccurate or incomplete data</li>
          <li>Right to erasure ("right to be forgotten") — request that we delete your data</li>
          <li>Right to restrict processing — request that we limit how we process your data</li>
          <li>Right to data portability — receive your data in a structured, commonly-used format</li>
          <li>Right to object — object to processing in certain circumstances</li>
          <li>Right not to be subject to automated decision-making that has significant effects on you</li>
          <li>(California residents) Right to know what categories of personal information we collect and how we use it</li>
          <li>(California residents) Right to non-discrimination for exercising any of these rights</li>
          <li>(California residents) Right to opt out of the sale of personal information — we do not sell your data, so there is nothing to opt out of</li>
        </ul>
        <p style="margin:0 0 10px;">To exercise these rights, contact us via the 🐛 Report page in Settings. We will respond within the timeframes required by applicable law (typically 30 days for GDPR, 45 days for CCPA).</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">8. Breach Notification</p>
        <p style="margin:0 0 10px;">In the event of a personal data breach that creates a significant risk to your rights and freedoms, we will notify affected users without undue delay. For users covered by GDPR (EU/UK), we will notify the relevant supervisory authority within 72 hours of becoming aware of the breach where required. Notifications will be sent via the email address on your account.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">9. External Links</p>
        <p style="margin:0 0 10px;">Our app contains links to external websites, mapping applications, music services (YouTube, Spotify, Apple Music), lyrics sites, venue pages, and friend-shared content. We have no control over, and assume no responsibility for, the content, privacy policies, or data practices of any third-party sites or services. Clicking an external link is your decision; we recommend reviewing the privacy policy of any site before submitting information to it.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">10. Changes</p>
        <p style="margin:0 0 10px;">We may update this policy at any time. Continued use of the service after changes are posted constitutes acceptance of the updated terms.</p>

        <p style="font-weight:700;color:var(--text);margin:14px 0 4px;">11. Contact</p>
        <p style="margin:0;">Questions? Use the 🐛 Report page in Settings.</p>
      </div>
    </div>
  </div>
</div>

<!-- ── SET VENUE SHEET (Apple-fy Phase A, 2026-06-06) ─────────────────────────
     Canonical "Where are you tonight?" sheet. Opens from every "set venue" entry
     point (header CTA, future Sing-card tap). Single source of truth — no modes,
     no Browse/My Venues toggle. Three implicit sections, rendered into
     #set-venue-body by _renderSetVenueSheet():
       1. Currently set (only if tonightVenue is set) — venue + ✓ + Clear link
       2. Suggestions (top 3 most-frequent from perf log, deduped)
       3. Around You — merged my-venues + DMV curated (via _venueSearch(''))
     Sticky search input filters all three at once (uses _venueSearch(q)).
     Tap any row -> setTonightVenue -> sheet closes. No Save button. -->
<div class="modal-overlay" id="set-venue-modal" onclick="closeModalOnBg(event,'set-venue-modal')">
  <div class="modal">
    <div class="modal-handle"></div>
    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;">
      <div class="modal-title" style="margin-bottom:0;">📍 Tonight's Venue</div>
    </div>
    <input class="form-input" id="set-venue-search" autocomplete="off"
      placeholder="Search venues…"
      style="width:100%;font-size:14px;padding:10px 12px;margin-bottom:14px;"
      oninput="_renderSetVenueSheet()">
    <div id="set-venue-body" style="display:flex;flex-direction:column;gap:14px;"></div>
    <div style="margin-top:18px;display:flex;gap:8px;">
      <button class="btn btn-secondary" style="flex:1;justify-content:center;"
        onclick="closeModal('set-venue-modal')">Cancel</button>
    </div>
  </div>
</div>

<!-- ── AREA SHEET (Venues Apple-fy Phase B, 2026-06-08) ────────────────────────
     The canonical control for "where am I browsing on Discover?" — answers
     three intents in one sheet: (1) Use the curated DMV list, (2) peek at a
     zip without changing home (the Yelp traveler model), (3) save a zip as
     home (the Yelp mover model). Replaces the four scattered set/change-home
     affordances. Distinct from #set-venue-modal (tonight's venue chip in the
     header) — that's "where I'm singing right now," this is "where I'm
     looking for places to sing." -->
<div class="modal-overlay" id="area-modal" onclick="closeModalOnBg(event,'area-modal')">
  <div class="modal">
    <div class="modal-handle"></div>
    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
      <div class="modal-title" style="margin-bottom:0;">Set your area</div>
    </div>
    <div style="font-size:12px;color:var(--text2);line-height:1.5;margin-bottom:14px;">
      Choose where Discover looks for venues. Peek at a zip without changing your home, or save it as your new default.
    </div>
    <div id="area-modal-body" style="display:flex;flex-direction:column;gap:12px;"></div>
    <div style="margin-top:16px;display:flex;gap:8px;">
      <button class="btn btn-secondary" style="flex:1;justify-content:center;"
        onclick="closeModal('area-modal')">Cancel</button>
    </div>
  </div>
</div>

<!-- ── SETLIST AI FILL SHEET (Sing Level 3b, 2026-06-06) ──────────────────────
     The slot-picker mechanic from the retired AI Search → Setcraft mode, lifted
     into a modal opened from the Setlist card's 🎬 AI button. The setlist
     planning surface now has ONE canonical home (the Setlist card on Sing →
     Log), with AI as an optional helper that summons this modal. State is
     `currentSetlist` (unchanged) — edits propagate. -->
<div class="modal-overlay" id="setlist-ai-fill-modal" onclick="closeModalOnBg(event,'setlist-ai-fill-modal')">
  <div class="modal">
    <div class="modal-handle"></div>
    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
      <div class="modal-title" style="margin-bottom:0;">🎬 AI Fill Setlist</div>
    </div>
    <div style="font-size:12px;color:var(--text2);margin-bottom:14px;">AI suggests 3 candidates per empty slot. You pick which one fits.</div>
    <div id="setlist-ai-fill-body" style="min-height:120px;"></div>
  </div>
</div>

<!-- "✨ Suggest Songs to Add" — the relocated Recommend tool (2026-06-07, Ideas→Find
     Step 1). Library-GROWTH lives in the Songbook now, not Sing → AI Search. Runs
     the proven suggest engine into this modal via _smartIO redirect; result cards'
     +Rep/+WS add to the library. -->
<div class="modal-overlay" id="suggest-songs-modal" onclick="closeModalOnBg(event,'suggest-songs-modal')">
  <div class="modal">
    <div class="modal-handle"></div>
    <div class="modal-title" style="margin-bottom:6px;">✨ Suggest Songs to Add</div>
    <div style="font-size:12px;color:var(--text2);line-height:1.5;margin-bottom:12px;">AI reviews your songbook to gauge your tastes, then suggests new songs for you. Add an optional steer, then tap ✨ Suggest to generate.</div>
    <div style="display:flex;gap:6px;margin-bottom:8px;">
      <input class="form-input" id="suggest-songs-prompt" placeholder='e.g. "more 90s", "easier ballads" (optional)' autocomplete="off"
        style="flex:1;min-width:0;padding:8px 12px;font-size:13px;" onkeydown="if(event.key==='Enter')runSuggestSongs()">
      <button class="btn btn-primary btn-sm" onclick="runSuggestSongs()" style="flex-shrink:0;">✨ Suggest</button>
    </div>
    <!-- 🕓 Past suggestions — opens the shared suggestion log into the results
         area (2026-06-08). Hidden until the log is non-empty (set in openSuggestSongs). -->
    <div style="display:flex;justify-content:flex-end;margin-bottom:8px;">
      <button id="suggest-history-link" onclick="openSuggestLog()" style="display:none;background:none;border:none;color:var(--text3);font-size:12px;cursor:pointer;padding:0;text-decoration:underline;text-underline-offset:2px;">🕓 Past suggestions</button>
    </div>
    <div id="suggest-songs-results" style="min-height:80px;"></div>
    <button class="btn btn-secondary" style="width:100%;justify-content:center;margin-top:12px;" onclick="closeModal('suggest-songs-modal')">Close</button>
  </div>
</div>

<!-- The Single AI Picker modal (Sing Level 3a) was REMOVED 2026-06-06 along
     with the 🔍 AI button on the Single card. Reason: real-world feedback —
     "for a single use case, the AI is actually fairly useless. it's just
     guessing based on past perfs / tags, which aren't great proxies for what I
     might want to sing in this moment." Replaced with a 🎲 dice button that
     does the honest thing: picks a random song from your repertoire,
     populates the Single input, tap again to reroll. AI for setlist work
     (where filling N slots IS genuinely AI-helpable) survives via the
     Setlist card's 🎬 AI Fill modal. -->

<!-- SONG MODAL -->
<div class="modal-overlay" id="song-modal" onclick="_closeSongModalOnBg(event)">
  <div class="modal">
    <div class="modal-scroll-body">
    <div class="modal-handle"></div>
    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;">
      <div class="modal-title" id="song-modal-title" style="margin-bottom:0;">Add Song</div>
    </div>

    <!-- Three ways to add a song, presented as ONE consistent set of pills so they
         read as the same class of action (2026-06-06): manual (＋ Add Song), 📋 Bulk
         Add, and 🎵 Identify (Shazam-style — listens ~10s, IDs the song, adds it;
         calls audioFabSongId(), closing this modal first so the result isn't
         stacked). The whole row hides in Edit mode (display:none in openEditSong),
         which also hides Identify — no separate toggle needed. _setSongModalTab only
         toggles the two FORM tabs' active state; Identify is a one-shot action and
         never shows "active". -->
    <div id="song-modal-tabs" style="display:flex;gap:6px;margin-bottom:14px;">
      <button id="song-modal-tab-one-btn" class="tab-sub-btn active"
        style="flex:1;font-size:12px;padding:6px 4px;"
        onclick="_setSongModalTab('one')">＋ Add Song</button>
      <button id="song-modal-tab-bulk-btn" class="tab-sub-btn"
        style="flex:1;font-size:12px;padding:6px 4px;"
        onclick="_setSongModalTab('bulk')">📋 Bulk Add</button>
      <button id="song-modal-shazam" class="tab-sub-btn"
        style="flex:1;font-size:12px;padding:6px 4px;"
        onclick="closeModal('song-modal');setTimeout(function(){ if(typeof audioFabSongId==='function') audioFabSongId(); },150);">🎵 Identify</button>
    </div>

    <!-- TAB: One Song (canonical Add Song form) -->
    <div id="song-modal-tab-one">
    <input type="hidden" id="song-edit-id">
    <div style="position:relative;margin-bottom:8px;">
      <input class="form-input" id="song-artist" placeholder="Artist *" autocomplete="off"
        style="font-size:13px;padding:7px 10px;width:100%;box-sizing:border-box;"
        oninput="onArtistInput(this.value);_markEditSongDirty()" onblur="onArtistBlur()">
      <div id="artist-suggestions" class="autocomplete-dropdown" style="display:none;"></div>
    </div>
    <div style="position:relative;margin-bottom:8px;">
      <label style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text3);margin-bottom:3px;">
        Song Title *
        <span id="song-lookup-spinner" style="display:none;width:10px;height:10px;border:2px solid var(--border);border-top-color:var(--accent3);border-radius:50%;animation:spin 0.7s linear infinite;"></span>
        <span id="song-lookup-badge" style="display:none;font-size:10px;color:var(--accent3);font-weight:600;letter-spacing:0.3px;">AI</span>
      </label>
      <input class="form-input" id="song-title" placeholder="e.g. Bohemian Rhapsody" autocomplete="off"
        style="font-size:13px;padding:7px 10px;width:100%;box-sizing:border-box;"
        oninput="filterSongDropdown(this.value);_markEditSongDirty()">
      <div id="song-suggestions" class="autocomplete-dropdown" style="display:none;"></div>
    </div>
    <div id="song-meta-strip" style="display:none;background:rgba(124,58,237,0.08);border:1px solid rgba(124,58,237,0.2);border-radius:8px;padding:8px 10px;margin-bottom:8px;font-size:12px;color:var(--text2);">
      <span id="meta-duration"></span>
      <span id="meta-genre-label"></span>
    </div>
    <div style="display:flex;gap:8px;margin-bottom:8px;">
      <div style="flex:1;">
        <label style="font-size:11px;color:var(--text3);display:block;margin-bottom:3px;">Songbook Status</label>
        <select class="form-select" id="song-status"
          style="font-size:13px;padding:7px 10px;width:100%;font-weight:600;"
          onchange="_updateSongStatusColor(this);_markEditSongDirty()">
          <option value="repertoire">Repertoire ✅</option>
          <option value="working">Workshop 🔧</option>
        </select>
      </div>
      <div style="flex:1;">
        <label style="font-size:11px;color:var(--text3);display:block;margin-bottom:3px;">Genre</label>
        <input class="form-input" id="song-genre" placeholder="e.g. Pop, Rock"
          style="font-size:13px;padding:7px 10px;width:100%;box-sizing:border-box;"
          oninput="_markEditSongDirty()">
      </div>
    </div>
    <div style="margin-bottom:8px;">
      <div id="tag-selector"></div>
    </div>
    <input class="form-input" id="song-key" placeholder="Key / Pitch Notes"
      style="font-size:13px;padding:7px 10px;width:100%;box-sizing:border-box;margin-bottom:8px;"
      oninput="_markEditSongDirty()">
    <textarea class="form-textarea" id="song-notes" placeholder="Performance notes, tips, crowd reaction…"
      style="font-size:13px;margin-bottom:8px;"
      oninput="_markEditSongDirty()"></textarea>
    <input class="form-input" id="song-custom-tag" placeholder="Custom tag (comma-separated)"
      style="font-size:13px;padding:7px 10px;width:100%;box-sizing:border-box;"
      oninput="_markEditSongDirty()">
    </div>

    <!-- TAB: Bulk Add (relocated from Settings → Start in 2026-05-26; same IDs
         so confirmPasteImport / previewPasteImport / importFiles / etc. work
         unchanged). Settings → Start now points users here via openBulkAddSong(). -->
    <div id="song-modal-tab-bulk" style="display:none;">
      <div style="font-size:13px;color:var(--text2);margin-bottom:10px;line-height:1.6;">
        Paste your song list, or attach files — text, CSV, or screenshots, several at once. AI cleans up messy formatting automatically. <span style="color:var(--text3);">(PDF or Word? Copy the text out and paste it, or screenshot the list and attach that.)</span>
      </div>
      <textarea class="form-textarea" id="paste-import-text"
        placeholder="Paste your list here, e.g.:&#10;&#10;Radiohead - Creep&#10;Black by Pearl Jam&#10;Bohemian Rhapsody (Queen)&#10;Mr. Brightside by The Killers&#10;&#10;Hundreds of songs are fine — just paste them all."
        style="min-height:180px;font-family:monospace;font-size:12px;resize:vertical;"></textarea>
      <div style="display:flex;gap:8px;margin-top:8px;align-items:center;flex-wrap:wrap;">
        <button class="btn btn-secondary btn-sm" onclick="document.getElementById('paste-import-file').click()">📁 Attach files / screenshots</button>
        <!-- accept="*/*" (2026-06-07) — the old `.txt,text/plain,image/*` whitelist
             SILENTLY greyed out the formats people actually keep lists in (.csv,
             .pdf, .docx, .rtf, Notes/Numbers exports): the file was unselectable
             with no error, the #1 "import didn't work and I can't say why" cause.
             importFiles() now reads any text-like file + guides for binary docs. -->
        <input type="file" id="paste-import-file" accept="*/*" multiple style="display:none" onchange="importFiles(event)">
        <select class="form-select" id="paste-import-status" style="flex:1;font-size:13px;min-width:140px;">
          <option value="repertoire">Add to Repertoire</option>
          <option value="working">Add to Workshop</option>
        </select>
      </div>
      <div id="screenshot-import-status" style="font-size:12px;color:var(--text2);margin-top:6px;display:none;"></div>
      <div style="display:flex;gap:8px;margin-top:8px;">
        <button class="btn btn-primary" style="flex:1;justify-content:center;" onclick="previewPasteImport()">Preview Import →</button>
      </div>
      <div id="paste-import-preview" style="display:none;margin-top:12px;">
        <div style="font-size:12px;font-weight:600;color:var(--text2);margin-bottom:6px;" id="paste-preview-summary"></div>
        <div id="paste-preview-list" style="max-height:220px;overflow-y:auto;border:1px solid var(--border);border-radius:var(--radius-sm);font-size:12px;"></div>
        <div style="display:flex;gap:8px;margin-top:10px;">
          <button class="btn btn-secondary" style="flex:1;justify-content:center;" onclick="document.getElementById('paste-import-preview').style.display='none'">Cancel</button>
          <button class="btn btn-primary" id="paste-confirm-btn" style="flex:1;justify-content:center;" onclick="confirmPasteImport()">✅ Add these songs</button>
        </div>
        <div id="paste-ai-upgrade" style="display:none;"></div>
      </div>
      <div id="paste-import-status-msg" style="font-size:12px;margin-top:8px;margin-bottom:8px;"></div>
    </div>

    </div>
    <!-- Footer hidden on Bulk tab (it has its own Preview / Confirm flow inline). -->
    <div class="modal-footer" id="song-modal-footer">
      <button class="btn btn-secondary" onclick="_confirmCloseEditSong()">Cancel</button>
      <button class="btn btn-primary" onclick="saveSong()">Save Song</button>
    </div>
  </div>
</div>

<!-- SONG DETAIL MODAL -->
<div class="modal-overlay" id="song-detail-modal" onclick="closeModalOnBg(event, 'song-detail-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
    <div class="modal-handle"></div>
    <div id="song-detail-content"></div>
    </div>
    <div class="modal-footer" style="padding-top:10px;">
      <button class="btn btn-secondary btn-sm" id="song-detail-move-btn" style="display:none;">Move</button>
      <button class="btn btn-secondary btn-sm" id="song-detail-edit-btn">Edit</button>
      <!-- Owner-only catalog edit button. Hidden by default; song-detail.js
           toggles display when EDIT_MODE is true (admin.js — owner + ?edit=1). -->
      <button class="btn btn-secondary btn-sm" id="song-detail-catalog-btn" style="display:none;border-color:rgba(232,57,90,0.5);color:var(--accent);">✨ Catalog</button>
      <button class="btn btn-danger btn-sm" id="song-detail-delete-btn">Delete</button>
    </div>
  </div>
</div>

<!-- PERSON MODAL -->
<div class="modal-overlay" id="person-modal" onclick="closeModalOnBg(event, 'person-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
    <div class="modal-handle"></div>
    <div style="margin-bottom:0;">
    <div class="modal-title" id="person-modal-title" style="margin-bottom:0;">Add Person</div>
    </div>
    <input type="hidden" id="person-edit-id">
    <div class="form-group">
      <label class="form-label">Name *</label>
      <input class="form-input" id="person-name" placeholder="e.g. Dave">
    </div>
    <div class="form-group">
      <label class="form-label">Where you met</label>
      <input class="form-input" id="person-met" placeholder="e.g. The Crow Bar">
    </div>
    <div class="form-group">
      <label class="form-label">Songs you've seen them sing</label>
      <input class="form-input" id="person-songs" placeholder="e.g. Don't Stop Believin', Sweet Caroline">
    </div>
    <div class="form-row">
      <div class="form-group">
        <label class="form-label">Phone</label>
        <input class="form-input" id="person-phone" type="tel" placeholder="e.g. 202-555-0100">
      </div>
      <div class="form-group">
        <label class="form-label">Email</label>
        <input class="form-input" id="person-email" type="email" placeholder="e.g. dave@email.com">
      </div>
    </div>
    <div class="form-group">
      <label class="form-label">Instagram</label>
      <input class="form-input" id="person-instagram" placeholder="e.g. @dave_sings">
    </div>
    <!-- Photo capability removed. The "📷 Take / Choose Photo" button
         used to let users snap a pic of someone they'd just met at
         karaoke. Even with benign intent the framing was concerning —
         "BAR encourages taking pictures of strangers" wasn't a values
         signal we wanted to ship. Users who want to remember someone's
         appearance can just describe it in the Notes field below.
         The data field p.photo is preserved in existing rows (won't be
         displayed anywhere now, just dormant) so users with existing
         photos don't lose data silently on this deploy. -->
    <div class="form-group">
      <label class="form-label">Notes</label>
      <textarea class="form-textarea" id="person-notes" placeholder="Voice type, favorite genre, what they looked like, anything memorable…"></textarea>
    </div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary" onclick="closeModal('person-modal')">Cancel</button>
      <button class="btn btn-danger" id="person-delete-btn" style="display:none;" onclick="deletePerson()">Delete</button>
      <button class="btn btn-secondary" id="person-vcard-btn" style="display:none;" onclick="exportVCard()">📇 Save to Contacts</button>
      <button class="btn btn-primary" onclick="savePerson()">Save to Building A Rockstar</button>
    </div>
  </div>
</div>

<!-- VENUE MODAL -->
<div class="modal-overlay" id="venue-modal" onclick="closeModalOnBg(event, 'venue-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
    <div class="modal-handle"></div>
    <div style="margin-bottom:0;">
    <div class="modal-title" id="venue-modal-title" style="margin-bottom:0;">Add Venue</div>
    </div>
    <input type="hidden" id="venue-edit-id">
    <div class="form-group">
      <label class="form-label">Venue Name *</label>
      <input class="form-input" id="venue-name" placeholder="e.g. The Crow Bar">
    </div>
    <div class="form-group">
      <label class="form-label">Address</label>
      <input class="form-input" id="venue-address" placeholder="e.g. 42 High Street, Melbourne">
    </div>
    <div class="form-group">
      <label class="form-label">Venue Type</label>
      <select class="form-select" id="venue-type" onchange="onVenueTypeChange()">
        <option value="karaoke">🎤 Karaoke</option>
        <option value="open_mic">🎙️ Open Mic</option>
        <option value="both">🎤 + 🎙️ Both</option>
      </select>
    </div>
    <div class="form-group" id="venue-system-row">
      <label class="form-label">Karaoke System</label>
      <select class="form-select" id="venue-system">
        <option value="karafun">KaraFun</option>
        <option value="other">Other</option>
      </select>
    </div>
    <div class="form-group">
      <label class="form-label">Schedule</label>
      <input class="form-input" id="venue-schedule" placeholder="e.g. Every Thursday 8pm – midnight">
    </div>
    <div class="form-group">
      <label class="form-label">Phone</label>
      <input class="form-input" id="venue-phone" type="tel" placeholder="e.g. 202-555-0100">
    </div>
    <div class="form-group">
      <label class="form-label">Notes</label>
      <textarea class="form-textarea" id="venue-notes" placeholder="Vibe, host name, tips…"></textarea>
    </div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary" onclick="closeModal('venue-modal')">Cancel</button>
      <button class="btn btn-danger" id="venue-delete-btn" style="display:none;" onclick="deleteVenue()">Delete</button>
      <button class="btn btn-primary" onclick="saveVenue()">Save</button>
    </div>
  </div>
</div>

<!-- 🔤 PICK A SONG — the primary log/stage surface, sized as a full-height
     "worksheet" (2026-06-08): the song list fills the viewport (was capped at
     340px in a content-sized sheet) so the most-used surface uses the screen. -->
<div class="modal-overlay" id="picker-modal" onclick="closeModalOnBg(event, 'picker-modal')">
  <div class="modal picker-modal-full">
    <div class="modal-scroll-body" style="position:relative;">
    <div class="modal-handle"></div>
    <div class="modal-title" id="picker-modal-title" style="margin-bottom:8px;">Pick a song</div>
    <div class="source-toggle-row">
      <button class="source-toggle rep on" id="picker-src-rep" onclick="togglePickerSource('rep')">
        🎵 Repertoire <span class="count" id="picker-rep-count"></span>
      </button>
      <button class="source-toggle work on" id="picker-src-ws" onclick="togglePickerSource('ws')">
        🔧 Workshop <span class="count" id="picker-ws-count"></span>
      </button>
    </div>
    <input class="search-input" style="margin-bottom:6px;width:100%;" placeholder="Search or type any song…" oninput="renderSongPicker(this.value)" id="picker-search">
    <!-- ＋ Filters — browse control (always). -->
    <button class="btn btn-sm" id="picker-tag-filter-btn" onclick="openPickerFilterModal()"
      style="width:100%;justify-content:center;margin-bottom:6px;padding:4px 2px;font-size:11px;border-radius:20px;background:rgba(34,211,238,0.1);border:1px solid rgba(34,211,238,0.35);color:#67e8f9;">＋ Filters</button>
    <!-- 🎲 Surprise + the Log MODE (Tonight ctx only). The mode is a SEGMENTED
         Solo | Duet toggle, NOT a single "Duet" button — naming the "Solo"
         alternative + the "Log as" label make it unmistakably about how the
         ⚡ Log is recorded, not a filter for duet songs. (2026-06-08) -->
    <div id="picker-action-row" style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
      <button class="picker-act-btn" onclick="_pickerRandom()" title="Pick a random song from the current list" style="flex:0 0 auto;min-width:42px;">🎲</button>
      <span class="picker-logmode-lbl" id="picker-logmode-lbl" style="margin-left:auto;">Log as</span>
      <div class="picker-logmode" title="How your ⚡ Log is recorded — solo or a duet">
        <button id="picker-solo-btn" class="picker-logmode-seg on" onclick="_pickerSetDuet(false)">Solo</button>
        <button id="picker-duet-btn" class="picker-logmode-seg" onclick="_pickerSetDuet(true)">🎭 Duet</button>
      </div>
    </div>
    <div id="picker-tag-filter-active-chain" style="display:none;margin-bottom:6px;"></div>
    <!-- Compact icon legend (2026-06-09) — the per-row ⚡/＋ buttons are icon-only,
         and on touch the title= tooltips never appear, so a user got no heads-up
         what they did. Tiny 10px line; populated per-ctx by renderSongPicker's
         #picker-modal-sub write (Tonight: ⚡ log / ＋ Backstage · Setlist: ＋ add). -->
    <div id="picker-modal-sub" style="font-size:10px;color:var(--text3);line-height:1.3;margin:0 0 6px;"></div>
    <!-- List + A–Z jump rail. The list fills the viewport (max-height scales with
         screen height, 2026-06-08); the rail stays beside it + jumps to sections. -->
    <div class="picker-az-wrap">
      <div class="song-picker-list" id="song-picker-list"></div>
      <div class="picker-rail" id="picker-rail"></div>
    </div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-primary" onclick="closeModal('picker-modal')" style="flex:1;">Done</button>
    </div>
  </div>
</div>

<!-- REQUEST SONG MODAL (friend-to-friend song requests — renamed from
     'Suggest' since the request/suggest distinction was collapsed
     earlier; element IDs kept for backwards compat) -->
<div class="modal-overlay" id="suggest-song-modal" onclick="closeModalOnBg(event,'suggest-song-modal')">
  <div class="modal">
    <div class="modal-scroll-body" style="position:relative;">
      <div class="modal-handle"></div>
      <div class="modal-title" id="suggest-modal-title" style="display:flex;align-items:center;gap:10px;"><span id="suggest-modal-icon"></span><span>Request Solo</span></div>
      <div style="font-size:12px;color:var(--text3);margin-bottom:12px;" id="suggest-modal-sub"></div>
      <!-- Selected song chip (hidden until selection) -->
      <div id="suggest-selected" style="display:none;align-items:center;gap:8px;padding:8px 12px;background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.3);border-radius:var(--radius-sm);margin-bottom:10px;font-size:12px;">
        <span style="color:var(--success);font-weight:700;">✓</span>
        <span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text);"><strong id="suggest-selected-title"></strong> · <span id="suggest-selected-artist" style="color:var(--text2);"></span></span>
        <button onclick="_clearSuggestSelection()" style="background:none;border:none;color:var(--text3);font-size:14px;cursor:pointer;padding:0 4px;line-height:1;" title="Clear selection">✕</button>
      </div>
      <!-- Autocomplete search input — checks friend's songbook first, then iTunes -->
      <div class="form-group" style="margin-bottom:10px;">
        <input class="form-input" id="suggest-search" placeholder="Songbook or manual…" autocomplete="off" oninput="_onSuggestSearchInput(this.value)">
      </div>
      <!-- Results area: friend's songbook matches → iTunes when no matches -->
      <div id="suggest-results" style="margin-bottom:12px;max-height:280px;overflow-y:auto;border:1px solid var(--border);border-radius:var(--radius-sm);"></div>
      <!-- Optional note -->
      <div class="form-group" style="margin-bottom:0;">
        <label class="form-label">Note (optional)</label>
        <input class="form-input" id="suggest-friend-note" placeholder="e.g. Perfect for your range!" maxlength="300">
      </div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary" onclick="closeModal('suggest-song-modal')" style="flex:none;">Cancel</button>
      <button class="btn btn-primary" id="suggest-send-btn" onclick="submitFriendSongSuggestion()" style="flex:1;justify-content:center;" disabled>Send</button>
    </div>
  </div>
</div>

<!-- DUET PICKER MODAL — shows the intersection of my songbook ∩ friend's
     songbook (rep+ws, subject to their share prefs). Optional filter to
     the subset tagged 'Duet' in my library. Each row sends a duet
     request via song_requests (request_type='duet'). -->
<div class="modal-overlay" id="duet-picker-modal" onclick="closeModalOnBg(event,'duet-picker-modal')">
  <div class="modal">
    <div class="modal-scroll-body" style="position:relative;">
      <div class="modal-handle"></div>
      <div class="modal-title" id="duet-modal-title" style="display:flex;align-items:center;gap:10px;"><span id="duet-modal-icon"></span><span>Request Duet</span></div>
      <div style="font-size:12px;color:var(--text3);margin-bottom:14px;" id="duet-modal-sub">Songs you both have. Tap to send a duet request.</div>
      <label style="display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text2);cursor:pointer;margin-bottom:10px;padding:8px 10px;background:rgba(244,114,182,0.06);border:1px solid rgba(244,114,182,0.2);border-radius:var(--radius-sm);">
        <input type="checkbox" id="duet-tag-only" onchange="renderDuetIntersection()" style="accent-color:#f472b6;width:15px;height:15px;cursor:pointer;">
        Show only songs you've tagged <strong style="color:#f472b6;">Duet</strong>
      </label>
      <!-- Local filter on the intersection. Plain text-substring match
           against title + artist. Does NOT search iTunes (a duet requires
           both parties to own the song, so iTunes results are irrelevant
           here). ANDs with the duet-tag checkbox above. -->
      <div class="form-group" style="margin-bottom:10px;">
        <input class="form-input" id="duet-search" placeholder="Filter songs in common…" autocomplete="off" oninput="_onDuetSearchInput(this.value)">
      </div>
      <div id="duet-intersection-list" style="border:1px solid var(--border);border-radius:var(--radius-sm);max-height:50vh;overflow-y:auto;"></div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary" onclick="closeModal('duet-picker-modal')" style="flex:1;justify-content:center;">Close</button>
    </div>
  </div>
</div>

<!-- CATALOG EDIT MODAL — owner-only editor for the central song_catalog
     row. Backed by the song-catalog-edit Edge Function. Surfaced via the
     "✨ Edit catalog entry" button in the song detail modal, which only
     renders when EDIT_MODE is on (admin.js — requires ?edit=1 in URL). -->
<div class="modal-overlay" id="catalog-edit-modal" onclick="closeModalOnBg(event,'catalog-edit-modal')">
  <div class="modal">
    <div class="modal-scroll-body" style="position:relative;">
      <div class="modal-handle"></div>
      <div class="modal-title" style="display:flex;align-items:center;gap:10px;">
        <span style="font-size:22px;line-height:1;">✨</span>
        <span>Edit catalog entry</span>
      </div>
      <div id="catalog-edit-title" style="font-size:13px;color:var(--text2);margin-bottom:10px;"></div>
      <div id="catalog-edit-audit" style="font-size:11px;color:var(--text3);margin-bottom:14px;padding:6px 10px;background:rgba(255,255,255,0.03);border-radius:var(--radius-sm);"></div>

      <!-- Tri-state chip legend -->
      <div style="font-size:10px;color:var(--text3);margin-bottom:8px;line-height:1.5;">
        <strong style="color:var(--text2);">Click any tag to cycle:</strong>
        <span style="color:var(--accent);">filled = on (locked 🔒)</span> ·
        <span>strikethrough = blocked (🚫 from AI)</span> ·
        <span>outline = AI may add</span>
      </div>

      <!-- Performance tags -->
      <div style="font-size:12px;font-weight:600;color:var(--text);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;">Performance tags</div>
      <div id="catalog-edit-perf-chips" style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:16px;"></div>

      <!-- Voice technique tags -->
      <div style="font-size:12px;font-weight:600;color:var(--text);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;">Voice techniques</div>
      <div id="catalog-edit-voice-chips" style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:16px;"></div>

      <!-- Singular metadata fields -->
      <div style="font-size:12px;font-weight:600;color:var(--text);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px;">Metadata</div>
      <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:6px;">
        <div>
          <label class="form-label" style="font-size:10px;">Album</label>
          <input class="form-input" id="catalog-edit-album" oninput="_catMarkDirty()" style="font-size:12px;padding:6px 8px;">
        </div>
        <div>
          <label class="form-label" style="font-size:10px;">Year</label>
          <input class="form-input" id="catalog-edit-year" type="number" min="1900" max="2099" oninput="_catMarkDirty()" style="font-size:12px;padding:6px 8px;">
        </div>
        <div>
          <label class="form-label" style="font-size:10px;">Genre</label>
          <input class="form-input" id="catalog-edit-genre" oninput="_catMarkDirty()" style="font-size:12px;padding:6px 8px;">
        </div>
        <div>
          <label class="form-label" style="font-size:10px;">Duration (ms)</label>
          <input class="form-input" id="catalog-edit-duration-ms" type="number" min="0" oninput="_catMarkDirty()" style="font-size:12px;padding:6px 8px;">
        </div>
      </div>
      <div style="margin-bottom:12px;">
        <label class="form-label" style="font-size:10px;">Artwork URL</label>
        <input class="form-input" id="catalog-edit-artwork-url" oninput="_catMarkDirty()" style="font-size:12px;padding:6px 8px;">
      </div>

      <!-- Frozen fields panel -->
      <div style="font-size:10px;color:var(--text3);margin-bottom:6px;">🔒 Lock against future AI overwrites:</div>
      <div style="display:flex;flex-wrap:wrap;gap:10px;font-size:11px;color:var(--text2);">
        <label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="catalog-edit-frozen-album"       onchange="_catMarkDirty()"> album</label>
        <label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="catalog-edit-frozen-year"        onchange="_catMarkDirty()"> year</label>
        <label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="catalog-edit-frozen-genre"       onchange="_catMarkDirty()"> genre</label>
        <label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="catalog-edit-frozen-artwork_url" onchange="_catMarkDirty()"> artwork</label>
        <label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="catalog-edit-frozen-duration_ms" onchange="_catMarkDirty()"> duration</label>
      </div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary" onclick="_confirmCloseCatalogEdit()" style="flex:none;">Cancel</button>
      <button class="btn btn-primary" id="catalog-edit-save-btn" onclick="saveCatalogEdit()" style="flex:1;justify-content:center;">Save</button>
    </div>
  </div>
</div>

<!-- ── External data files ───────────────────────────────────────────── -->
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script src="technique_guide.js"></script>
<script src="tag-rubric.js"></script>
<script src="trophy-edit.js"></script>
<script src="artist_db.js"></script>
<script src="venues_db.js"></script>
<script src="audio_fab.js"></script>
<script src="tagger.js"></script>
<script src="utils.js"></script>
<script src="piano.js"></script>
<script src="recording-studio.js"></script>
<script src="trophy.js"></script>
<script src="perf-modal.js"></script>
  <script src="songbook.js"></script>
  <script src="autocomplete.js"></script>
  <script src="song-modal.js"></script>
  <script src="requests.js"></script>
  <script src="smart-search.js"></script>
  <script src="setlist.js"></script>
  <script src="suggestions.js"></script>
  <script src="venues.js"></script>
  <script src="bug-report.js"></script>
  <script src="enrichment.js"></script>
  <script src="admin.js"></script>
  <script src="artist-catalog.js"></script>
  <script src="song-detail.js"></script>
  <script src="catalog-admin.js"></script>
  <script src="perf-entry.js"></script>
  <script src="tagger-ui.js"></script>

  <script src="nav.js"></script>
  <script src="settings-nav.js"></script>
  <script src="modal-swipe.js"></script>
  <script src="misc.js"></script>
  <script src="sizzle-reel.js"></script>
  <script src="find.js"></script>
<!-- ── Main app ──────────────────────────────────────────────────────── -->
<script>
// ─── DATA ───────────────────────────────────────────────────────────────────

// FILTER vocabulary (13 chips). Superset of the AI vocabulary: the AI tagger
// reads PERF_TAG_HELP's 12 keys (room-dynamics only); `Deep Cut` rides along
// here as a USER-APPLIED-only chip (AI can't judge obscurity — computed later
// from popularity / in-app perf-frequency). Easy/Difficult retired to the
// Voice axis + paid tier; Duet→Duet/Group; Camp added (2026-06-03 revamp).
const PERF_TAGS = ['Opener','Slow Ballad','Power Ballad','Rock Out','Fun/Dance','Camp','Showstopper','Deep Cut','Duet/Group','Last Call','Crowd Work','Anthem','Big Ending'];
const SYSTEM_TAGS = ['KaraFun ✓','__ai__']; // internal tags — stored on songs but never shown as filter chips
const TAGS = PERF_TAGS; // alias used elsewhere
const VOX_RANGE_TAGS = ['Vox ↑↑','Vox ↑','Vox →','Vox ↓','Vox ↓↓'];
const ERA_TAGS = ['50s/60s','70s','80s','90s','2000s','2010s','2020s'];

// ─── VOCAL TECHNIQUES ────────────────────────────────────────────────────────
// ─── technique_guide.js loaded externally ──────────────────────────────────

function load(key, def) {
  try { const v = localStorage.getItem(key); return v ? JSON.parse(v) : def; } catch { return def; }
}
function save(key, val) {
  localStorage.setItem(key, JSON.stringify(val));
  // Timestamp every local write so cloudPull can detect unsynced changes.
  localStorage.setItem('kk_local_ts_' + key, String(Date.now()));
  // Debounced: rapid writes to the same key collapse into one DB push after 1 s.
  if (typeof debouncedCloudPush === 'function') debouncedCloudPush(key, val);
  // Mirror perf-log changes to the append-only `performances` Supabase
  // table. See utils.js save() for the canonical comment.
  if (key === 'kk_songs' && typeof _diffAndSyncPerfs === 'function') {
    _diffAndSyncPerfs(val);
  }
}

// ── Refresh all in-memory state from localStorage after a cloudPull ───────────
// Called by auth.js on startup so the UI reflects cloud data without a reload.
// Safe to call at any time; render functions are guarded with typeof checks.
function _refreshAppStateFromCloud() {
  try {
    // Re-read every top-level state variable from the freshly-pulled localStorage
    songs            = load('kk_songs', []);
    customTags       = load('kk_custom_tags', []);
    // Re-run perf-tag rename/dedupe migration after cloudPull. The
    // bootstrap migration only touches the localStorage snapshot at
    // page-load time; cloud-synced state replaces it later and was
    // previously skipping the migration entirely.
    if (typeof _migrateSongsInPlace === 'function') _migrateSongsInPlace();
    if (typeof _migrateCustomTagsInPlace === 'function') _migrateCustomTagsInPlace();
    // Dedupe on every cloudPull too — a stale device could have written
    // dupes during the pre-fix window, and we want those to self-heal
    // the next time fresh cloud data lands locally.
    if (typeof _dedupeSongsInPlace === 'function') _dedupeSongsInPlace();
    people           = load('kk_people', []);
    venues           = load('kk_venues', []);
    setlists         = load('kk_setlists', []);
    currentSetlist   = load('kk_current_setlist', { name: '', songs: [] });
    suggestions      = load('kk_suggestions', []);
    requestQueue     = load('kk_requests', []);
    friendSuggestions = load('kk_friend_suggestions', []);
    suggestionLog    = load('kk_suggestion_log', []);
    // earnedAwards uses a normalisation step (v1 stored plain strings, v2 objects)
    const _rawAwards = load('kk_awards', []);
    earnedAwards = _rawAwards.length > 0 && typeof _rawAwards[0] === 'string'
      ? _rawAwards.map(id => ({ id, unlockedAt: null, venue: null }))
      : _rawAwards;

    // Reconcile derived trophy state against current perfs after every cloudPull.
    // Trophy unlocks only fire from checkAwards() which runs on perf-log events
    // — so kk_awards can drift from kk_songs when a sync hiccupped (PWA
    // background-kill, cloud clobber, etc). Re-running here is idempotent
    // (checkAwards skips already-earned), and any newly-detected unlocks fire
    // their own save('kk_awards',...) which pushes the repaired state to cloud.
    // Self-healing: one device's recompute fixes all friends' read of her trophies.
    if (typeof checkAwards === 'function') {
      try { checkAwards(); } catch (e) { console.warn('[KK] checkAwards on cloudPull failed:', e); }
    }

    // Re-render the main UI surfaces
    if (typeof renderSongs           === 'function') renderSongs();
    if (typeof renderWorkingOn       === 'function') renderWorkingOn();
    if (typeof renderTagFilters      === 'function') renderTagFilters();
    if (typeof renderWorkingTagFilters === 'function') renderWorkingTagFilters();
    // 2026-05-23 — kk_artist_overrides is now cloud-synced. If the FVP
    // browse (Vox Gym → Famous Voices) is currently open, re-render
    // so cross-device review-state edits / technique corrections appear
    // without requiring a tab switch.
    const vgpProfilesEl = document.getElementById('vgp-profiles');
    if (vgpProfilesEl && vgpProfilesEl.style.display !== 'none' && typeof renderVgpArtistCatalog === 'function') {
      try { renderVgpArtistCatalog(); } catch (e) { /* swallow */ }
    }

    console.log('[KK] _refreshAppStateFromCloud: state updated —',
                songs.length, 'songs,', people.length, 'people,', venues.length, 'venues');
  } catch (e) {
    console.warn('[KK] _refreshAppStateFromCloud failed:', e);
  }
}

let songs = load('kk_songs', []);
let customTags = load('kk_custom_tags', []);
let people = load('kk_people', []);
// ─── artist_db.js loaded externally ───────────────────────────────────────
// ─── MIGRATION: strip removed tags + normalise genres ────────────────────────
// Added "Vox ↑/→/↓" 3-bucket arrows to the strip list. They used to be
// in PERF_TAGS but were retired when the 5-tier vox classification
// shipped (filter chips derive from FVP artist vox + paid-tier per-song
// vocalLow/vocalHigh; the AI-generated 3-bucket arrows were unused).
// The AI tagger was still mandating one of these arrows per song for a
// while; PERF_SET filtered them out before they reached song.tags, but
// historical data (rows written when the arrows WERE in PERF_TAGS) still
// has them lingering. Strip them so they stop polluting the Edit Song
// modal's Custom drawer.
// 'Easy' / 'Difficult' retired 2026-06-03: vocal-DEMAND is a Voice-axis
// property + the paid per-voice tier, not a room-dynamics perf tag. Strip
// them from user songs (the auto-tagger no longer produces them either).
const REMOVED_TAGS = ['Crowd Pleaser', 'Warm Up', 'Vox ↑', 'Vox →', 'Vox ↓', 'Easy', 'Difficult'];
const PERF_TAG_MAP = {
  'Rocker': 'Growl/Scream',
  'Closer': 'Last Call',
  'Crowd Participation': 'Anthem',
  'Duet': 'Duet/Group',   // 2026-06-03 — widened to cover 3+ group numbers
};
// 2026-06-03 perf→theme consolidation. When the user-facing Themes category
// shipped + the rubric expansion landed, Funny + Sexy moved from PERF_TAGS
// to THEME_TAGS as Humor + Sensuality (content-meaning belongs with themes,
// not perf-function). Migration: rewrite any song's tags arrays to drop
// these perf tags, and add the corresponding theme to the parallel themes
// arrays. _migrateSongsInPlace handles bootstrap + cloudPull paths. The
// catalog gets the same rewrite via SQL — see
// supabase/migrations/song_catalog_funny_sexy_consolidation.sql.
const PERF_TO_THEME_MAP = {
  'Funny': 'Humor',
  'Sexy': 'Sensuality',
};
const GENRE_MAP = {
  // Cleanup: old migration targets that were never in GENRE_TAGS
  'Rock/Hard Rock': 'Hard Rock',
  'Pop/Pop Rock': 'Pop',
  'Heavy Metal': 'Metal',
  // iTunes sub-genre normalisation → canonical names
  'Pop Rock': 'Pop',
  'Pop & Pop Rock': 'Pop',
  'Hard Rock': 'Hard Rock',
  'Alternative Rock': 'Alternative',
  'Alternative Metal': 'Metal',
  'Southern Rock': 'Hard Rock',
  'Latin Rock': 'Latin',
  'Reggae Pop': 'Reggae',
  'Rock/Hip Hop': 'Hard Rock',
  'Country Pop': 'Pop',
  'Country Rock': 'Country',
  'Folk Rock': 'Folk',
  'Jazz Pop': 'Jazz',
  // Tag renames
  'Alt/Indie': 'Alternative',
  'Broadway/Musical': 'Soundtrack/Musical',
};
// Migration runs on every load (bootstrap) AND after cloudPull
// (_refreshAppStateFromCloud) — extracted into a function so cloud-synced
// data passes through the same rename/filter/dedupe path as locally-cached
// data. Without this, a song whose cloud state had a retired tag (e.g.
// 'Crowd Participation') would only get fixed on bootstrap; cloudPull
// replaces in-memory songs with fresh cloud data and the bootstrap
// migration doesn't re-run, so the stale tag re-surfaces post-pull.
//
// Also migrates _manualTags + _suppressedTags so the auto-tagger's manual
// edit protection logic correctly matches against the renamed tag names.
// If a user's _manualTags still has 'Crowd Participation', force-retag
// would strip 'Anthem' (because manual.has('Anthem') === false), unraveling
// the lock-in guarantee.
function _migrateSongsInPlace() {
  let migrated = false;
  function _migrateTagList(list) {
    if (!Array.isArray(list)) return { list, changed: false };
    const before = list.join('|');
    let next = list.filter(t => !REMOVED_TAGS.includes(t));
    next = next.map(t => PERF_TAG_MAP[t] || t);
    next = [...new Set(next)]; // dedupe — old + new name may co-exist
    return { list: next, changed: next.join('|') !== before };
  }
  // Perf→theme migration for the Funny/Sexy consolidation (2026-06-03). Pulls
  // any perf-tag-to-theme entries OUT of the tag list and returns BOTH the
  // shrunk tag list AND the themes to be added. Caller adds them to the
  // parallel theme list (s.themes / _manualThemes / _suppressedThemes).
  function _extractPerfToTheme(list) {
    if (!Array.isArray(list)) return { list, extractedThemes: [], changed: false };
    const themes = [];
    const next = [];
    for (const t of list) {
      const themeName = PERF_TO_THEME_MAP[t];
      if (themeName) themes.push(themeName);
      else next.push(t);
    }
    return { list: next, extractedThemes: themes, changed: themes.length > 0 };
  }
  function _mergeThemes(existing, additions) {
    const list = Array.isArray(existing) ? existing : [];
    if (!additions.length) return { list, changed: false };
    const before = list.join('|');
    const next = [...new Set([...list, ...additions])];
    return { list: next, changed: next.join('|') !== before };
  }
  songs.forEach(s => {
    if (!s.tags) s.tags = [];
    // 1) Funny/Sexy → Humor/Sensuality consolidation (BEFORE the
    //    REMOVED_TAGS/PERF_TAG_MAP pass so the extraction sees them).
    const perfToThemeR = _extractPerfToTheme(s.tags);
    if (perfToThemeR.changed) {
      s.tags = perfToThemeR.list;
      const themesR = _mergeThemes(s.themes, perfToThemeR.extractedThemes);
      if (themesR.changed) s.themes = themesR.list;
      migrated = true;
    }
    if (Array.isArray(s._manualTags)) {
      const manExtract = _extractPerfToTheme(s._manualTags);
      if (manExtract.changed) {
        s._manualTags = manExtract.list;
        const manThemes = _mergeThemes(s._manualThemes, manExtract.extractedThemes);
        if (manThemes.changed) s._manualThemes = manThemes.list;
        migrated = true;
      }
    }
    if (Array.isArray(s._suppressedTags)) {
      const supExtract = _extractPerfToTheme(s._suppressedTags);
      if (supExtract.changed) {
        s._suppressedTags = supExtract.list;
        const supThemes = _mergeThemes(s._suppressedThemes, supExtract.extractedThemes);
        if (supThemes.changed) s._suppressedThemes = supThemes.list;
        migrated = true;
      }
    }

    // 2) Normal REMOVED_TAGS + PERF_TAG_MAP rewrite (existing migration path).
    const tagsR = _migrateTagList(s.tags);
    if (tagsR.changed) { s.tags = tagsR.list; migrated = true; }
    if (Array.isArray(s._manualTags)) {
      const manR = _migrateTagList(s._manualTags);
      if (manR.changed) { s._manualTags = manR.list; migrated = true; }
    }
    if (Array.isArray(s._suppressedTags)) {
      const supR = _migrateTagList(s._suppressedTags);
      if (supR.changed) { s._suppressedTags = supR.list; migrated = true; }
    }
    if (s.genre) {
      const mapped = s.genre.split(',').map(g => {
        const trimmed = g.trim();
        return GENRE_MAP[trimmed] || trimmed;
      }).filter(Boolean);
      const deduped = [...new Set(mapped)];
      const newGenre = deduped.join(', ');
      if (newGenre !== s.genre) { s.genre = newGenre; migrated = true; }
    }
  });
  if (migrated) save('kk_songs', songs);
  return migrated;
}

// Clean stale entries from kk_custom_tags. saveSong's old path could push
// any tag the user "had on" the song into customTags if it wasn't in a
// canonical category list — meaning a post-rename song with 'Crowd
// Participation' still in its tags array could pollute customTags on save
// and surface as a Custom drawer chip ever after. Drop entries that are
// now PERF_TAG_MAP keys (the old names) — they're no longer "custom",
// they're renamed perf tags.
function _migrateCustomTagsInPlace() {
  const before = customTags.length;
  customTags = customTags.filter(t => !(t in PERF_TAG_MAP) && !REMOVED_TAGS.includes(t));
  if (customTags.length !== before) {
    save('kk_custom_tags', customTags);
    return true;
  }
  return false;
}

_migrateSongsInPlace();
_migrateCustomTagsInPlace();
_dedupeSongsInPlace();

// ─── MIGRATION: dedupe songs by (title, artist) ───────────────────────────────
// Songs were originally allowed to duplicate across statuses (a song could
// exist as both 'working' and 'repertoire' if added twice) — three creation
// paths slipped past the dedup gate that the other paths used. This
// migration self-heals existing duplicates by merging them into a single
// canonical entry per (lowercase title, lowercase artist) pair, and remaps
// any setlist references so the kept song's ID is used everywhere.
//
// Merge rules:
//   - Status: repertoire beats working (status escalation, not downgrade)
//   - Performances: concatenated, deduped by pid (or date+venue fallback),
//                   sorted chronologically
//   - tags / _manualTags / _suppressedTags / vocalTechniques: set-union
//   - favourite / __ai__: boolean OR
//   - Scalar fields (genre, key, year, album, etc): keep first non-empty
//   - notes: concat with separator if both non-empty
//
// Runs at bootstrap AND after cloudPull (mirrors _migrateSongsInPlace).
function _dedupeSongsInPlace() {
  if (!Array.isArray(songs) || songs.length === 0) return false;
  const _key = s => `${(s.title || '').toLowerCase().trim()}||${(s.artist || '').toLowerCase().trim()}`;
  const seen = new Map();        // key -> keeper song
  const idRemap = new Map();     // dupe.id -> keeper.id (for setlist remap)
  const toRemove = new Set();    // dupe ids to drop
  for (const s of songs) {
    if (!s.title || !s.id) continue;
    const k = _key(s);
    if (!k.startsWith('||')) {
      if (!seen.has(k)) {
        seen.set(k, s);
      } else {
        const keeper = seen.get(k);
        _mergeSongInto(keeper, s);
        idRemap.set(s.id, keeper.id);
        toRemove.add(s.id);
      }
    }
  }
  if (toRemove.size === 0) return false;
  songs = songs.filter(s => !toRemove.has(s.id));
  save('kk_songs', songs);

  // Remap song IDs in saved setlists + current setlist. The global
  // `setlists` / `currentSetlist` arrays may not be initialized yet at
  // bootstrap (admin.js loads later), so we go through localStorage.
  const _setlists = load('kk_setlists', []);
  let _sLMutated = false;
  for (const sl of _setlists) {
    if (!Array.isArray(sl.songs)) continue;
    const remapped = sl.songs.map(id => idRemap.get(id) || id);
    // Also dedupe within a setlist (don't show same song twice in a row).
    const compact = [];
    const seenInSL = new Set();
    for (const id of remapped) {
      // Free-text entries (prefixed 'free_') aren't deduped by id.
      if (typeof id === 'string' && id.startsWith('free_')) { compact.push(id); continue; }
      if (seenInSL.has(id)) continue;
      seenInSL.add(id);
      compact.push(id);
    }
    if (compact.length !== sl.songs.length || compact.some((id, i) => id !== sl.songs[i])) {
      sl.songs = compact;
      _sLMutated = true;
    }
  }
  if (_sLMutated) save('kk_setlists', _setlists);

  const _cur = load('kk_current_setlist', { name: '', songs: [] });
  if (Array.isArray(_cur.songs)) {
    const remapped = _cur.songs.map(id => idRemap.get(id) || id);
    const compact = [];
    const seenInCur = new Set();
    for (const id of remapped) {
      if (typeof id === 'string' && id.startsWith('free_')) { compact.push(id); continue; }
      if (seenInCur.has(id)) continue;
      seenInCur.add(id);
      compact.push(id);
    }
    if (compact.length !== _cur.songs.length || compact.some((id, i) => id !== _cur.songs[i])) {
      _cur.songs = compact;
      save('kk_current_setlist', _cur);
    }
  }

  console.log('[KK] _dedupeSongsInPlace: merged', toRemove.size, 'duplicate song(s)');
  return true;
}

function _mergeSongInto(keeper, dupe) {
  // Status escalation — repertoire beats working, never the reverse.
  if (keeper.status === 'working' && dupe.status === 'repertoire') {
    keeper.status = 'repertoire';
  }
  // Performances: concatenate + dedupe by pid (or date+venue fallback).
  const allPerfs = [...(Array.isArray(keeper.performances) ? keeper.performances : []),
                    ...(Array.isArray(dupe.performances)   ? dupe.performances   : [])];
  const perfSeen = new Set();
  const mergedPerfs = [];
  for (const p of allPerfs) {
    if (!p) continue;
    const pkey = p.pid || `${p.date || ''}|${p.venue || ''}`;
    if (perfSeen.has(pkey)) continue;
    perfSeen.add(pkey);
    mergedPerfs.push(p);
  }
  mergedPerfs.sort((a, b) => new Date(a.date || 0) - new Date(b.date || 0));
  keeper.performances = mergedPerfs;
  // Set-union tag lists.
  const _union = (a, b) => [...new Set([...(Array.isArray(a) ? a : []), ...(Array.isArray(b) ? b : [])])];
  keeper.tags             = _union(keeper.tags,             dupe.tags);
  keeper._manualTags      = _union(keeper._manualTags,      dupe._manualTags);
  keeper._suppressedTags  = _union(keeper._suppressedTags,  dupe._suppressedTags);
  keeper.vocalTechniques  = _union(keeper.vocalTechniques,  dupe.vocalTechniques);
  // Boolean OR.
  keeper.favourite = !!(keeper.favourite || dupe.favourite);
  keeper.__ai__    = !!(keeper.__ai__    || dupe.__ai__);
  // Scalar fields: keep first non-empty from the keeper, fall back to dupe.
  const _scalars = ['genre', 'key', 'year', 'album', 'artworkUrl', 'duration',
                    'singer', 'vocalLow', 'vocalHigh', 'tessitura'];
  for (const f of _scalars) {
    if ((keeper[f] === undefined || keeper[f] === '' || keeper[f] === null) && dupe[f]) {
      keeper[f] = dupe[f];
    }
  }
  // Notes: concat with separator if both non-empty.
  if (keeper.notes && dupe.notes && keeper.notes.trim() !== dupe.notes.trim()) {
    keeper.notes = `${keeper.notes}\n\n${dupe.notes}`;
  } else if (!keeper.notes && dupe.notes) {
    keeper.notes = dupe.notes;
  }
  // itunesSearched: keep most-progressed ('v2' > truthy > falsy).
  if (dupe.itunesSearched === 'v2') keeper.itunesSearched = 'v2';
  else if (!keeper.itunesSearched && dupe.itunesSearched) keeper.itunesSearched = dupe.itunesSearched;
  // audio_assets: concatenate.
  if (Array.isArray(dupe.audio_assets) && dupe.audio_assets.length > 0) {
    keeper.audio_assets = [...(Array.isArray(keeper.audio_assets) ? keeper.audio_assets : []),
                           ...dupe.audio_assets];
  }
  // metadata: shallow merge, keeper wins on conflict.
  if (dupe.metadata && typeof dupe.metadata === 'object') {
    keeper.metadata = { ...dupe.metadata, ...(keeper.metadata || {}) };
  }
}

// ─── MIGRATION: old vocalTechniques slugs → current REG-/RES-/TEX-/PHR- format ─
// Maps every pre-taxonomy slug to its canonical current ID.
// Runs on every load; only saves if something actually changed.
const _TECH_SLUG_MAP = {
  'growl-distortion':    'TEX-growl',
  'open-throat':         'TEX-covered',
  'covered-voice':       'TEX-covered',
  'chest-conversational':'REG-modal',
  'vibrato':             'TEX-vibrato-fast',
  'fry-scream':          'TEX-fry',
};
const _STALE_PEAK_REGISTERS = new Set(Object.keys(_TECH_SLUG_MAP));
let vocalMigrated = false;
songs.forEach(s => {
  if (!s.vocalTechniques) s.vocalTechniques = [];
  // Migrate retired Growl/Scream perf tag → TEX-growl vocal technique.
  // (PERF_TAG_MAP earlier turns the older 'Rocker' tag into 'Growl/Scream'
  // first, so handling Growl/Scream here covers both retired tags.) Also
  // strips Growl/Scream from s.tags so it doesn't linger as a dead chip.
  if ((s.tags||[]).includes('Growl/Scream')) {
    if (!s.vocalTechniques.includes('TEX-growl')) {
      s.vocalTechniques.push('TEX-growl');
    }
    s.tags = s.tags.filter(t => t !== 'Growl/Scream');
    vocalMigrated = true;
  }
  // Remap stale technique IDs to current format
  const remapped = s.vocalTechniques.map(t => _TECH_SLUG_MAP[t] || t);
  // Deduplicate (remap can cause duplicates e.g. two old slugs → same new slug)
  const deduped = [...new Set(remapped)];
  if (deduped.join(',') !== s.vocalTechniques.join(',')) {
    s.vocalTechniques = deduped;
    vocalMigrated = true;
  }
  // Remap stale peakRegister (texture/ambiguous slugs don't map cleanly to register; default REG-modal)
  if (s.peakRegister && _STALE_PEAK_REGISTERS.has(s.peakRegister)) {
    s.peakRegister = 'REG-modal';
    vocalMigrated = true;
  }
});
if (vocalMigrated) save('kk_songs', songs);

// ─── MIGRATION: stable pid + allowReactions on every performance ─────────────
// Future features (friend reactions, audience thumbs, per-perf tags, photo
// attachments) need a stable handle on each performance. Stamp a pid on any
// performance that doesn't already have one. allowReactions defaults to false
// — opt-in only — until the UI to set it exists.
let perfMigrated = false;
songs.forEach(s => {
  if (!Array.isArray(s.performances)) return;
  s.performances.forEach(p => {
    if (!p.pid)                         { p.pid = uid();           perfMigrated = true; }
    if (p.allowReactions === undefined) { p.allowReactions = false; perfMigrated = true; }
    if (p.rating === undefined)         { p.rating = null;          perfMigrated = true; }
  });
});
if (perfMigrated) save('kk_songs', songs);

// One device-side snapshot of kk_songs per local day. If the user opens BAR
// today and tonight's session ends with a cloud-sync race that corrupts their
// kk_songs, the snapshot taken at app open represents "this morning's good
// state" — recoverable via Settings → 📂 Your Data → Recover Songbook.
if (typeof snapshotKkSongs === 'function') snapshotKkSongs('startup');

// Render the header's persistent venue indicator once at startup so the
// "Venue: No venue" empty state shows from first paint, before any user
// interaction with the venue picker.
if (typeof renderActiveVenueIndicator === 'function') renderActiveVenueIndicator();

// ─── MIGRATION: setlists get a venue field ───────────────────────────────────
// Defaults to '' (no specific venue). New saves capture tonightVenue at save
// time. Existing setlists default to venue-less; user can re-save with a
// tonight venue active to associate them.
let setlistMigrated = false;
if (Array.isArray(setlists)) {
  setlists.forEach(sl => {
    if (sl.venue === undefined) { sl.venue = ''; setlistMigrated = true; }
  });
}
if (setlistMigrated) save('kk_setlists', setlists);

// ─── MIGRATION: Artist name normalization ────────────────────────────────────
const ARTIST_RENAMES = {
  'Florence + the Machine': 'Florence and the Machine',
  'Florence + The Machine': 'Florence and the Machine',
  'Fugees / Roberta Flack': 'Fugees',
  'Dua lipa': 'Dua Lipa',
};
let artistMigrated = false;
songs.forEach(s => {
  if (s.artist && ARTIST_RENAMES[s.artist]) {
    s.artist = ARTIST_RENAMES[s.artist];
    artistMigrated = true;
  }
});
if (artistMigrated) save('kk_songs', songs);

function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2); }

// ─── INIT ────────────────────────────────────────────────────────────────────

// Pull-to-refresh on mobile auto-restores the prior scroll position by
// default, so reloading mid-list dumps the user a few items down rather
// than at the top. Disable the browser's scroll restoration and force
// scroll-to-top on every load so refreshes feel like fresh starts.
try { if ('scrollRestoration' in history) history.scrollRestoration = 'manual'; } catch (_) {}
window.scrollTo(0, 0);

// Screenshot Mode body class — set early so the header badge's version
// number is hidden before first paint when capturing screenshots.
if (typeof isScreenshotMode === 'function' && isScreenshotMode()) {
  document.body.classList.add('bar-screenshot-mode');
}

renderTagFilters();
renderSongs();
renderWorkingTagFilters();
renderWorkingOn();

// First-session orientation cards (2026-05-31). Idempotent — silently
// shows or hides each card based on its kk_orient_<page>_dismissed flag.
if (typeof _renderAllOrientationCards === 'function') _renderAllOrientationCards();

// Vocal Tip card (2026-05-31). Surfaces one VOCAL_TECHNIQUES entry per
// day on the Stats tab as a discoverability hook back to Vox Gym.
// Idempotent — picks today's tip from localStorage if already set, else
// rolls a new one. Hides if user dismissed for today.
if (typeof _renderVocalTipCard === 'function') _renderVocalTipCard();

// Sunday nudge — if it's Sunday and no export in 7 days
(function checkSundayNudge() {
  const isSunday = new Date().getDay() === 0;
  const lastExport = parseInt(localStorage.getItem('kk_last_export') || '0');
  const daysSince = (Date.now() - lastExport) / (1000 * 60 * 60 * 24);
  const snoozed = localStorage.getItem('kk_nudge_snoozed') === new Date().toDateString();
  if (isSunday && daysSince > 7 && songs.length > 0 && !snoozed) {
    setTimeout(() => showExportNudge("It's Sunday — good time to save a backup of your data!"), 3000);
  }
})();
</script>

<div id="award-drawer">
  <div class="award-drawer-handle"></div>
  <div class="award-drawer-body">
    <div id="award-preview-card" class="award-card-share">
      <div class="award-drawer-icon" id="award-notif-icon">🏆</div>
      <div class="award-drawer-label">Achievement Unlocked</div>
      <div class="award-drawer-name" id="award-notif-name"></div>
      <div class="award-drawer-desc" id="award-notif-desc"></div>
    </div>
    <div style="display:flex;gap:8px;justify-content:center;">
      <button id="award-action-btn" class="btn btn-primary btn-sm" onclick="shareAward()">📤 Share</button>
      <button class="btn btn-secondary btn-sm" onclick="closeAwardDrawer()">Dismiss</button>
    </div>
  </div>
</div>

<!-- ── Stats share card (Frame-2 distribution) ────────────────────────────
     Hidden 1080×1920 render-source for the Stats Overview "📤 Share my stats"
     poster. shareStatsCard() fills the slot, html2canvas rasterizes this
     element → navigator.share (native sheet on mobile) / download fallback.
     Kept permanently OFF-SCREEN (#stats-share-stage), NOT display:none —
     html2canvas needs real layout. Fixed perimeter = the recognizable BAR
     "filter look" (red L-bracket + neon-spotlight bg + context pill + the
     KARAOKE billboard footer, the acquisition bridge for non-users). The
     content slot is the only thing that varies card-to-card. Wordmark is
     SOLID (not gradient-clipped) — html2canvas mangles background-clip:text. -->
<div id="stats-share-stage" aria-hidden="true">
  <div id="stats-share-card" class="ssc">
    <div class="ssc-pill">BAR &#47;&#47; CAREER STATS</div>
    <div class="ssc-slot" id="stats-share-slot"></div>
    <div class="ssc-footer">
      <div class="ssc-foot-left">
        <div class="ssc-mark">BAR</div>
        <div class="ssc-tag">THE KARAOKE COMPANION</div>
      </div>
      <div class="ssc-foot-url">BUILDINGAROCKSTAR.COM</div>
    </div>
  </div>
</div>

<!-- ── Venue welcome drawer — fires on first log of the night at a venue ─── -->
<div id="venue-welcome-drawer">
  <div class="award-drawer-handle"></div>
  <div class="award-drawer-body">
    <div id="venue-welcome-card" class="venue-welcome-card">
      <div id="venue-welcome-banner" class="venue-welcome-banner">WELCOME TO</div>
      <div id="venue-welcome-name" class="venue-welcome-name"></div>
      <div id="venue-welcome-meta" class="venue-welcome-meta"></div>
    </div>
    <div id="venue-welcome-ctas" style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap;"></div>
  </div>
</div>


<!-- ── AUDIO FAB — REMOVED (2026-06-09) ────────────────────────────────────────
     The last floating button is gone (zero-float milestone). Its two jobs were
     re-homed natively: (1) Record Vocal Memo → the inline #mv-rec-btn widget in
     Vox Gym → My Voice → Voice Notes (audioFabRecord/_setRecUI drive it). (2)
     Ambient Song ID → already lives in the Add Song modal (🎵 Identify, calls
     audioFabSongId). The audio_fab.js functions stay exported; _setOpen/_setIdUI
     null-guard the absent FAB elements, and modal-swipe.js's FAB-hide refs are
     optional-chained no-ops. -->

<!-- ── VIRTUAL PIANO ─────────────────────────────────────────────────────────
     The floating piano FAB was removed (2026-06-06 Apple-fy Phase 4) — the pitch
     keyboard is a Vox Gym tool, so its entry point moved into the Vox Gym tab
     ("🎹 Pitch Keyboard" button). togglePiano()/openPiano() (piano.js) are global
     and open the self-contained #piano-sheet. The #piano-fab CSS is left in place
     (harmless, matches nothing) so the social-regression friend-detail-hide pin
     and the null-safe modal-swipe references stay valid. -->

<!-- ── VIRTUAL PIANO BOTTOM SHEET ───────────────────────────────────────── -->
<div id="piano-sheet">
  <div id="piano-sheet-inner">
    <div class="piano-sheet-handle" id="piano-sheet-handle"></div>
    <div class="piano-sheet-header">
      <div class="piano-sheet-title">🎹 Reference Piano</div>
    </div>

    <!-- Portrait: octave shift controls -->
    <div class="piano-oct-controls" id="piano-oct-controls">
      <button class="piano-oct-btn" id="piano-oct-down" onclick="shiftOctave(-1)">◀ Lower</button>
      <span class="piano-oct-label" id="piano-oct-label">Octave 3 (C3–C4)</span>
      <button class="piano-oct-btn" id="piano-oct-up" onclick="shiftOctave(1)">Higher ▶</button>
    </div>

    <!-- Portrait: note grid -->
    <div id="piano-note-grid"></div>
  </div>

  <!-- Landscape: visual keyboard (full bleed, 2 octaves from current octave, scrollable) -->
  <div id="piano-keyboard-wrap" style="display:none;">
    <div id="piano-keyboard"></div>
  </div>

  <!-- Rotate hint (portrait only) -->
  <div id="piano-rotate-hint" style="display:none;">
    <span style="margin-left:16px;">🔄 Rotate to landscape for visual keyboard</span>
  </div>
</div>

<!-- AUTH MODAL -->
<!-- ── SONG ID CONFIRMATION MODAL ─────────────────────────────────────────── -->
<div class="modal-overlay" id="song-id-modal" onclick="closeModalOnBg(event,'song-id-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div class="modal-title">🎤 Song Identified</div>
      <div style="text-align:center;padding:24px 0 20px;">
        <div style="font-size:10px;letter-spacing:1.5px;color:var(--text3);margin-bottom:10px;text-transform:uppercase;">Detected Track</div>
        <div id="song-id-result-title"  style="font-size:28px;font-weight:700;color:var(--text);line-height:1.15;margin-bottom:6px;"></div>
        <div id="song-id-result-artist" style="font-size:16px;color:var(--text2);"></div>
      </div>
      <div class="modal-footer">
        <button class="btn btn-secondary" style="flex:1;justify-content:center;"
                onclick="closeModal('song-id-modal')">Cancel</button>
        <button class="btn btn-primary"   style="flex:1;justify-content:center;"
                onclick="confirmSongIdAdd()">Add to Workshop 🔧</button>
      </div>
    </div>
  </div>
</div>

<!-- FIND MODAL (universal in-app search, opened by header 🔍 button).
     Searches Songs / Friends / Venues / Trophies / Settings / Vox Gym /
     Help. Live results grouped by category, click-to-navigate. See find.js
     for the search algorithm + per-surface helpers + handler cache. -->
<div class="modal-overlay" id="find-modal" onclick="closeModalOnBg(event,'find-modal')">
  <div class="modal">
    <div class="modal-scroll-body" style="position:relative;">
      <div class="modal-handle"></div>
      <div class="modal-title" style="display:flex;align-items:center;gap:10px;"><span>🔍</span><span>Find anything</span></div>
      <div style="font-size:12px;color:var(--text3);margin-bottom:12px;">Search Songs, Friends, Venues, Trophies, Settings, Vox Gym, Help.</div>
      <input class="form-input" id="find-input" placeholder="Start typing…" autocomplete="off" oninput="_onFindInput(this.value)" style="margin-bottom:12px;font-size:14px;">
      <div id="find-results" style="border:1px solid var(--border);border-radius:var(--radius-sm);min-height:200px;max-height:55vh;overflow-y:auto;overflow-x:hidden;"></div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary" onclick="closeFindModal()" style="flex:1;justify-content:center;">Close</button>
    </div>
  </div>
</div>

<!-- DATA MODAL (import + export) -->
<div class="modal-overlay" id="data-modal" onclick="closeModalOnBg(event,'data-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div style="margin-bottom:16px;">
        <div class="modal-title" style="margin-bottom:0;">📂 Your Data</div>
      </div>

      <!-- ── IMPORT ─────────────────────────────────────────── -->
      <div style="font-size:10px;font-weight:700;letter-spacing:1px;color:var(--text3);text-transform:uppercase;margin-bottom:8px;">↓ Import</div>
      <button onclick="triggerImport();"
        style="width:100%;display:flex;align-items:center;gap:14px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;margin-bottom:4px;cursor:pointer;text-align:left;">
        <span style="font-size:26px;flex-shrink:0;">📥</span>
        <div>
          <div style="font-size:14px;font-weight:700;color:var(--text);margin-bottom:3px;">Load Backup</div>
          <div style="font-size:11px;color:var(--text3);line-height:1.5;">Upload a JSON file to restore your data.<br>New songs merge in; duplicates are skipped.</div>
        </div>
      </button>

      <button onclick="openSnapshotRestore();"
        style="width:100%;display:flex;align-items:center;gap:14px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;margin-top:8px;margin-bottom:4px;cursor:pointer;text-align:left;">
        <span style="font-size:26px;flex-shrink:0;">🔄</span>
        <div>
          <div style="font-size:14px;font-weight:700;color:var(--text);margin-bottom:3px;">Recover Songbook</div>
          <div style="font-size:11px;color:var(--text3);line-height:1.5;">Roll your songbook (songs + performance log) back to an earlier day.<br>Up to 7 daily snapshots kept automatically on this device and in the cloud.</div>
        </div>
      </button>

      <!-- ── divider ────────────────────────────────────────── -->
      <div style="border-top:1px solid var(--border);margin:14px 0 12px;"></div>

      <!-- ── EXPORT ─────────────────────────────────────────── -->
      <div style="font-size:10px;font-weight:700;letter-spacing:1px;color:var(--text3);text-transform:uppercase;margin-bottom:8px;">↑ Export</div>

      <!-- Full backup -->
      <button onclick="exportData();closeModal('data-modal');"
        style="width:100%;display:flex;align-items:center;gap:14px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;margin-bottom:6px;cursor:pointer;text-align:left;">
        <span style="font-size:26px;flex-shrink:0;">📦</span>
        <div>
          <div style="font-size:14px;font-weight:700;color:var(--text);margin-bottom:3px;">Full Backup</div>
          <div style="font-size:11px;color:var(--text3);line-height:1.5;">All songs, perfs, venues, tags, profile, friends &amp; settings · JSON<br>Re-import into any Building A Rockstar instance</div>
        </div>
      </button>
      <!-- Optional heavy-data toggles. Default OFF since both can balloon
           the backup. Read by exportData() in bug-report.js. -->
      <div style="background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:10px;font-size:11px;color:var(--text3);">
        <div style="margin-bottom:5px;font-weight:600;color:var(--text2);">Include heavy data (optional):</div>
        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin-bottom:4px;">
          <input type="checkbox" id="backup-include-audio" style="accent-color:var(--accent);width:14px;height:14px;cursor:pointer;">
          <span>🎙️ Voice memo audio (can be several MB)</span>
        </label>
        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
          <input type="checkbox" id="backup-include-messages" style="accent-color:var(--accent);width:14px;height:14px;cursor:pointer;">
          <span>💬 Message history (can be large)</span>
        </label>
      </div>

      <!-- Songbook JSON -->
      <button onclick="exportSongbookJSON();closeModal('data-modal');"
        style="width:100%;display:flex;align-items:center;gap:14px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;margin-bottom:10px;cursor:pointer;text-align:left;">
        <span style="font-size:26px;flex-shrink:0;">📋</span>
        <div>
          <div style="font-size:14px;font-weight:700;color:var(--text);margin-bottom:3px;">Songbook Only (JSON)</div>
          <div style="font-size:11px;color:var(--text3);line-height:1.5;">Songs &amp; tags · merges cleanly into<br>another user's existing catalog</div>
        </div>
      </button>

      <!-- Songbook PDF (via browser print) -->
      <button onclick="exportSongbookPDF();closeModal('data-modal');"
        style="width:100%;display:flex;align-items:center;gap:14px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;margin-bottom:4px;cursor:pointer;text-align:left;">
        <span style="font-size:26px;flex-shrink:0;">📄</span>
        <div>
          <div style="font-size:14px;font-weight:700;color:var(--text);margin-bottom:3px;">Songbook (PDF)</div>
          <div style="font-size:11px;color:var(--text3);line-height:1.5;">Opens the print dialog with a formatted repertoire · sorted by artist<br>Pick "Save as PDF" to save, or send to a real printer</div>
        </div>
      </button>
    </div>
  </div>
</div>
<!-- SNAPSHOT RESTORE MODAL -->
<div class="modal-overlay" id="snap-restore-modal" onclick="closeModalOnBg(event,'snap-restore-modal')">
  <div class="modal" style="max-height:80vh;">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div style="margin-bottom:14px;">
        <div class="modal-title" style="margin-bottom:6px;">🔄 Recover Songbook</div>
        <div style="font-size:12px;color:var(--text2);line-height:1.5;">
          BAR auto-saves a daily snapshot of your songbook (songs + their performance log)
          on this device, and (when signed in) mirrors it to the cloud. If something gets
          clobbered, pick a date below to roll back. Your current state is snapshotted
          first, so you can undo. <strong style="color:var(--text);">Other data</strong>
          (venues, friends, setlists, voice memos, settings) is not affected — use
          Backup &amp; Restore for a full-app rollback.
        </div>
      </div>
      <div style="font-size:11px;color:var(--text3);text-transform:uppercase;letter-spacing:0.6px;font-weight:700;margin-bottom:6px;">📱 On this device</div>
      <div id="snap-restore-list" style="margin-bottom:18px;"></div>
      <div style="font-size:11px;color:var(--text3);text-transform:uppercase;letter-spacing:0.6px;font-weight:700;margin-bottom:6px;">☁️ Cloud snapshots</div>
      <div id="cloud-snap-restore-list" style="margin-bottom:14px;"></div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="closeModal('snap-restore-modal')">Close</button>
    </div>
  </div>
</div>
<!-- FILTER HELP MODAL -->
<div class="modal-overlay" id="filter-help-modal" onclick="closeModalOnBg(event,'filter-help-modal')">
  <div class="modal" style="max-height:80vh;">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div id="filter-help-content" style="margin-bottom:14px;"></div>
      <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="closeModal('filter-help-modal')">Close</button>
    </div>
  </div>
</div>

<!-- SONGBOOK TAG FILTER MODAL — replaces the legacy in-page
     #tag-filter-drawer. Modal pattern frees the sticky nav and gives each
     tag category its own row so chip expansions never compete for space. -->
<div class="modal-overlay" id="songbook-filter-modal" onclick="closeModalOnBg(event,'songbook-filter-modal')">
  <div class="modal" style="max-height:90vh;display:flex;flex-direction:column;padding:0;">
    <div style="padding:18px 20px 0;">
      <div class="modal-handle"></div>
      <div style="display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:4px;">
        <div class="modal-title" style="margin-bottom:0;">Sort &amp; Filter</div>
        <div id="songbook-filter-modal-count" style="font-size:12px;color:var(--text2);font-family:'DM Sans',sans-serif;white-space:nowrap;"></div>
      </div>
      <div style="font-size:11px;color:var(--text3);line-height:1.5;margin-bottom:8px;">Within a category: OR or AND. Between categories: always AND.</div>
    </div>
    <div id="songbook-filter-modal-body" style="flex:1;overflow-y:auto;padding:0 20px 14px;-webkit-overflow-scrolling:touch;"></div>
    <div style="padding:12px 20px 24px;border-top:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;">
      <button onclick="_clearAllSongbookFilters()" style="background:none;border:none;color:var(--text3);font-size:12px;text-decoration:underline;cursor:pointer;padding:6px 4px;">Clear all</button>
      <button id="songbook-filter-modal-apply" onclick="closeSongbookFilterModal()" class="btn btn-primary" style="flex:1;justify-content:center;">✓ Apply</button>
    </div>
  </div>
</div>

<!-- RANDOMISER TAG FILTER MODAL — same structure as Songbook's. -->
<div class="modal-overlay" id="rand-filter-modal" onclick="closeModalOnBg(event,'rand-filter-modal')">
  <div class="modal" style="max-height:90vh;display:flex;flex-direction:column;padding:0;">
    <div style="padding:18px 20px 0;">
      <div class="modal-handle"></div>
      <div style="display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:4px;">
        <div class="modal-title" style="margin-bottom:0;">Tag Restrictions</div>
        <div id="rand-filter-modal-count" style="font-size:12px;color:var(--text2);font-family:'DM Sans',sans-serif;white-space:nowrap;"></div>
      </div>
      <div style="font-size:11px;color:var(--text3);line-height:1.5;margin-bottom:8px;">Within a category: OR or AND. Between categories: always AND.</div>
    </div>
    <div id="rand-filter-modal-body" style="flex:1;overflow-y:auto;padding:0 20px 14px;-webkit-overflow-scrolling:touch;"></div>
    <div style="padding:12px 20px 24px;border-top:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;">
      <button onclick="_clearAllRandFilters()" style="background:none;border:none;color:var(--text3);font-size:12px;text-decoration:underline;cursor:pointer;padding:6px 4px;">Clear all</button>
      <button id="rand-filter-modal-apply" onclick="closeRandFilterModal()" class="btn btn-primary" style="flex:1;justify-content:center;">✓ Apply</button>
    </div>
  </div>
</div>

<!-- PICKER (Setlist Add) TAG FILTER MODAL. Stacks on top of the picker
     modal — bottom-sheet over bottom-sheet, each shows a visible top
     edge so there's no visual claustrophobia. -->
<div class="modal-overlay" id="picker-filter-modal" onclick="closeModalOnBg(event,'picker-filter-modal')">
  <div class="modal" style="max-height:75vh;display:flex;flex-direction:column;padding:0;">
    <div style="padding:18px 20px 0;">
      <div class="modal-handle"></div>
      <div style="display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:4px;">
        <div class="modal-title" style="margin-bottom:0;">Tag Filters</div>
        <div id="picker-filter-modal-count" style="font-size:12px;color:var(--text2);font-family:'DM Sans',sans-serif;white-space:nowrap;"></div>
      </div>
      <div style="font-size:11px;color:var(--text3);line-height:1.5;margin-bottom:8px;">Within a category: OR or AND. Between categories: always AND.</div>
    </div>
    <div id="picker-filter-modal-body" style="flex:1;overflow-y:auto;padding:0 20px 14px;-webkit-overflow-scrolling:touch;"></div>
    <div style="padding:12px 20px 24px;border-top:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;">
      <button onclick="_clearAllPickerFilters()" style="background:none;border:none;color:var(--text3);font-size:12px;text-decoration:underline;cursor:pointer;padding:6px 4px;">Clear all</button>
      <button id="picker-filter-modal-apply" onclick="closePickerFilterModal()" class="btn btn-primary" style="flex:1;justify-content:center;">✓ Apply</button>
    </div>
  </div>
</div>

<!-- SMART SEARCH / SUGGEST TAG FILTER MODAL. -->
<div class="modal-overlay" id="smart-filter-modal" onclick="closeModalOnBg(event,'smart-filter-modal')">
  <div class="modal" style="max-height:90vh;display:flex;flex-direction:column;padding:0;">
    <div style="padding:18px 20px 0;">
      <div class="modal-handle"></div>
      <div style="display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:4px;">
        <div class="modal-title" style="margin-bottom:0;">Tag Restrictions</div>
        <div id="smart-filter-modal-count" style="font-size:12px;color:var(--text2);font-family:'DM Sans',sans-serif;white-space:nowrap;"></div>
      </div>
      <div style="font-size:11px;color:var(--text3);line-height:1.5;margin-bottom:8px;">Within a category: OR or AND. Between categories: always AND.</div>
    </div>
    <div id="smart-filter-modal-body" style="flex:1;overflow-y:auto;padding:0 20px 14px;-webkit-overflow-scrolling:touch;"></div>
    <div style="padding:12px 20px 24px;border-top:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;">
      <button onclick="_clearAllSmartFilters()" style="background:none;border:none;color:var(--text3);font-size:12px;text-decoration:underline;cursor:pointer;padding:6px 4px;">Clear all</button>
      <button id="smart-filter-modal-apply" onclick="closeSmartFilterModal()" class="btn btn-primary" style="flex:1;justify-content:center;">✓ Apply</button>
    </div>
  </div>
</div>

<!-- HELP MODAL -->
<div class="modal-overlay" id="help-modal" onclick="closeModalOnBg(event,'help-modal')">
  <div class="modal" style="max-height:88vh;">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div style="margin-bottom:14px;">
        <div class="modal-title" style="margin-bottom:0;">❓ Building A Rockstar Guide</div>
      </div>

      <!-- Search -->
      <div style="position:relative;margin-bottom:14px;">
        <span style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--text3);font-size:13px;pointer-events:none;">🔍</span>
        <input id="help-search" type="search" class="form-input" placeholder="Search help…"
          oninput="helpSearch(this.value)"
          style="padding-left:32px;height:36px;font-size:13px;">
      </div>

      <!-- No-results message (hidden until search finds nothing) -->
      <div id="help-no-results" style="display:none;text-align:center;color:var(--text3);font-size:13px;padding:16px 0;">No sections match your search.</div>

      <!-- Table of Contents -->
      <div id="help-toc" style="background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px 14px;margin-bottom:20px;">
        <div style="font-size:11px;font-weight:700;color:var(--text3);letter-spacing:1px;text-transform:uppercase;margin-bottom:8px;">Get Started</div>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:12px;line-height:1.7;">
          <a href="#help-start"       onclick="return helpTocGo('help-start');"       style="color:var(--accent2);text-decoration:none;">1. Getting Started</a>
          <a href="#help-tour"        onclick="return helpTocGo('help-tour');"        style="color:var(--accent2);text-decoration:none;">2. The Highlight Reel</a>
          <a href="#help-walkthrough" onclick="return helpTocGo('help-walkthrough');" style="color:var(--accent2);text-decoration:none;">3. Full Walkthrough</a>
        </div>

        <div style="font-size:11px;font-weight:700;color:var(--text3);letter-spacing:1px;text-transform:uppercase;margin:14px 0 8px;padding-top:10px;border-top:1px solid var(--border);">Reference · Doing &amp; Singing</div>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:12px;line-height:1.7;">
          <a href="#help-songbook"  onclick="return helpTocGo('help-songbook');"  style="color:var(--accent2);text-decoration:none;">📚 Songbook</a>
          <a href="#help-night"     onclick="return helpTocGo('help-night');"     style="color:var(--accent2);text-decoration:none;">🎤 Sing</a>
          <a href="#help-setlists"  onclick="return helpTocGo('help-setlists');"  style="color:var(--accent2);text-decoration:none;">📋 Setlists</a>
          <a href="#help-recording" onclick="return helpTocGo('help-recording');" style="color:var(--accent2);text-decoration:none;">🎬 Recording</a>
          <a href="#help-venues"    onclick="return helpTocGo('help-venues');"    style="color:var(--accent2);text-decoration:none;">📍 Venues</a>
          <a href="#help-voxgym"    onclick="return helpTocGo('help-voxgym');"    style="color:var(--accent2);text-decoration:none;">🎙️ Vox Gym</a>
        </div>

        <div style="font-size:11px;font-weight:700;color:var(--text3);letter-spacing:1px;text-transform:uppercase;margin:14px 0 8px;">Stats &amp; Social</div>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:12px;line-height:1.7;">
          <a href="#help-stats" onclick="return helpTocGo('help-stats');" style="color:var(--accent2);text-decoration:none;">📊 Stats</a>
          <a href="#help-people"   onclick="return helpTocGo('help-people');"   style="color:var(--accent2);text-decoration:none;">👥 Friends</a>
          <a href="#help-filters"  onclick="return helpTocGo('help-filters');"  style="color:var(--accent2);text-decoration:none;">🏷️ Filter Guide</a>
        </div>

        <div style="font-size:11px;font-weight:700;color:var(--text3);letter-spacing:1px;text-transform:uppercase;margin:14px 0 8px;">Account &amp; App</div>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:12px;line-height:1.7;">
          <a href="#help-sync"     onclick="return helpTocGo('help-sync');"     style="color:var(--accent2);text-decoration:none;">☁️ Cloud Sync</a>
          <a href="#help-import"   onclick="return helpTocGo('help-import');"   style="color:var(--accent2);text-decoration:none;">📂 Import &amp; Export</a>
          <a href="#help-settings" onclick="return helpTocGo('help-settings');" style="color:var(--accent2);text-decoration:none;">⚙️ Settings</a>
          <a href="#help-update"   onclick="return helpTocGo('help-update');"   style="color:var(--accent2);text-decoration:none;">↺ Updating</a>
          <a href="#" onclick="closeModal('help-modal');openLegalModal('tos');return false;" style="color:var(--accent2);text-decoration:none;">⚖️ Legal &amp; Privacy</a>
        </div>
      </div>

      <!-- Sections -->
      <div id="help-start" class="help-section" style="margin-bottom:22px;">
        <div style="font-size:14px;font-weight:700;color:var(--accent2);margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border);">1. Getting Started</div>
        <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;"><b style="color:var(--text);">Building A Rockstar</b> is for anyone who sings — whether you're a karaoke regular with a tight go-to list, an open-mic singer building a real set, or someone trying to take their voice further. If you sing, this is for you.</p>
        <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;">The free tier handles the busywork of being a singer: a smart, taggable songbook; setlists you can build and reorder for tonight's venue; a performance log that remembers what you sang where; AI that finds new songs you'll love; and crowd-tested picks from your favorite spots. No account required.</p>
        <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;">For singers who want to keep growing, BAR believes <b style="color:var(--text);">the stage is your training ground</b>. Karaoke and open-mic nights give you a real audience, real nerves, and real feedback — and every performance you log becomes data you can learn from. The paid tier goes deeper with personalized voice analysis and exercises matched to your actual voice, but it's there when you want it, not in the way when you don't.</p>
        <p style="font-size:13px;color:var(--accent2);font-weight:600;line-height:1.7;text-align:center;margin-bottom:14px;">Go ahead. Build A Rockstar.</p>
        <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;"><strong style="color:var(--text);">Cloud accounts enthusiastically recommended, but not required.</strong> When you're logged in, all of your data saves seamlessly to the cloud and syncs across devices, and creating an account is free. Still, no account is required, and BAR can be used entirely locally, saving to your own device. Just be sure to backup often.</p>
      </div>

      <div id="help-tour" class="help-section" style="margin-bottom:22px;">
        <div style="font-size:14px;font-weight:700;color:var(--accent2);margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border);">2. The Highlight Reel</div>
        <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;">A visual sizzle reel of everything BAR can do — no how-tos, just the highlights. Tap through to see the features at a glance, then dive into the full walkthrough below when you're ready to learn how to use them.</p>
        <button class="btn btn-primary btn-sm" onclick="openSizzleReel()" style="width:100%;justify-content:center;">▶ Launch the Highlight Reel</button>
      </div>

      <div id="help-walkthrough" class="help-section" style="margin-bottom:22px;">
        <div style="font-size:14px;font-weight:700;color:var(--accent2);margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border);">3. Full Walkthrough</div>
        <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;">While additional reference material is provided throughout this help section, to get the most out of BAR, tap the button below and take our full 5-minute walkthrough.</p>
        <button class="btn btn-primary btn-sm" style="width:100%;justify-content:center;"
          onclick="closeModal('help-modal');setTimeout(()=>openMorePage('sp-start'),50);">
          🚀 Open the full walkthrough →
        </button>
      </div>

      <div class="help-major-divider">Reference</div>

      <div class="help-category-title">Doing &amp; Singing</div>

      <div id="help-songbook" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">📚</span><span class="help-acc-title">Your Songbook</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">🟢 <strong style="color:var(--success);">Repertoire</strong> vs. 🟠 <strong style="color:var(--warning);">Workshop</strong> — every song lives in one of two buckets. <em>Repertoire</em> is what you're ready to perform tonight; <em>Workshop</em> is songs you're still learning or unsure about. To move a song between buckets, tap it open and use the Edit screen. The colored left-edge stripe on each card shows its current status.</div>
            <div style="margin-bottom:5px;">➕ <strong style="color:var(--text);">Add a song</strong> — tap the + button. Enter title, artist, key, tags, and notes.</div>
            <div style="margin-bottom:5px;">✨ <strong style="color:var(--text);">Suggest songs to add</strong> — tap <strong style="color:var(--accent3);">✨ Suggest</strong> and AI recommends new songs you don't own yet, based on the taste already in your songbook. Add an optional steer ("more 90s", "easier ballads"), then tap ＋ Rep / ＋ WS on any pick to add it. (This is where the old AI "Recommend" mode lives now — it's library-building, so it belongs with your Songbook.)</div>
            <div style="margin-bottom:5px;">✏️ <strong style="color:var(--text);">Edit</strong> — tap any song card, then Edit to modify details.</div>
            <div style="margin-bottom:5px;">🏷️ <strong style="color:var(--text);">Tags</strong> — label songs (e.g. "Rock", "Crowd Favorite", "Wedding"). Tap the <em>sliders icon</em> in the search bar to open <em>Sort &amp; Filter</em> and filter by one or more tags at once.</div>
            <div style="margin-bottom:5px;">🔍 <strong style="color:var(--text);">Search &amp; sort</strong> — the search bar filters by title or artist instantly; the sliders icon at its right opens Sort &amp; Filter (Artist, Title A–Z, Recently Added, Last Performed, Most Performed + tag filters).</div>
            <div style="margin-bottom:5px;">⭐ <strong style="color:var(--text);">Favorites</strong> — tap the star on any card to mark it as a favorite.</div>
            <div style="margin-bottom:5px;">🎵 <strong style="color:var(--text);">Song ID</strong> — tap the cyan Song/ID FAB (lower left on most screens) to auto-identify a song playing nearby and add it to your Songbook.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Try it now</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('songs');setTimeout(openAddSong,80);">＋ Add a Song</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('songs');setTimeout(openBulkAddSong,80);">📋 Bulk Add</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('songs');">📚 Open Songbook</button>
          </div>
        </div>
      </div>

      <div id="help-night" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">🎤</span><span class="help-acc-title">Sing</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">🎤 <strong style="color:var(--text);">Log Performances</strong> — the default view. Use Quick Log to search your library, pick a song, choose a venue, and tap <em>Log it</em>. Songs not in your library can be entered manually. Build and save setlists here too.</div>
            <div style="margin-bottom:5px;">🎲 <strong style="color:var(--text);">Randomizer</strong> — picks a random song from your library, filtered by tags, status, or era if you like.</div>
            <div style="margin-bottom:5px;">📥 <strong style="color:var(--text);">Requests</strong> — when a BAR friend asks you to sing a song, it appears in a <strong>📥 Requests</strong> section on the Tonight board (right under Up Next). Tap <strong style="color:var(--accent3);">＋ Up Next</strong> to queue it for tonight, or ✕ to dismiss. The full request log + a notepad for jotting crowd requests live behind the "Requests &amp; crowd notes" link on that board.</div>
            <div style="margin-bottom:5px;">🔍 <strong style="color:var(--text);">AI Search</strong> — AI-powered song finding. Describe a vibe, mood, or theme and AI searches your library and the internet for matches. Accepts tag restrictions, favourites, and a 🎤 KaraFun filter — and if you've uploaded your KaraFun catalog (More → Tags &amp; Tools), results are verified against your catalog so you only see songs you can actually sing tonight. Want AI to suggest brand-new songs to <em>add</em> to your library? That lives as <strong style="color:var(--accent3);">✨ Suggest</strong> in your Songbook now. AI is built into BAR — no setup required.</div>
            <div style="margin-bottom:5px;">🎴 <strong style="color:var(--text);">Result cards</strong> — each result shows the title, artist, and AI's reasoning. Inline badges convey at-a-glance info: a <span style="display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;background:var(--success);color:#0a0a0f;font-size:9px;font-weight:700;vertical-align:middle;">✓</span> means it's in your <strong style="color:var(--success);">Repertoire</strong>, amber means <strong style="color:var(--warning);">Workshop</strong>; <span style="font-size:10px;color:var(--accent2);background:rgba(255,107,53,0.12);border:1px solid rgba(255,107,53,0.4);border-radius:6px;padding:0 5px;">🎤 KF</span> means it matches your KaraFun catalog; <span style="font-size:10px;color:#a78bfa;background:rgba(124,58,237,0.12);border:1px solid rgba(124,58,237,0.35);border-radius:6px;padding:0 5px;">new</span> on the artist line means the artist isn't in your library yet. The AI's reasoning is clamped to two lines so cards stay scannable.</div>
            <div style="margin-bottom:5px;">◀ <strong style="color:var(--text);">Action drawer</strong> — every result card has a thin handle on its right edge. Tap it to slide open a small panel with the available actions. For songs you don't have, the panel holds <strong style="color:var(--success);">+</strong> (add to Repertoire), <strong style="color:var(--warning);">+</strong> (add to Workshop), and the <strong style="color:#FF0000;">YouTube</strong> + <strong style="color:#1DB954;">Spotify</strong> brand-mark buttons (tap to open a preview search in either platform). For songs already in your library, only the preview buttons show — the ✓ on the title row already says you have it. Tap the handle again (now pointing ▶) to close.</div>
            <div style="margin-bottom:5px;">🎬 <strong style="color:var(--text);">Recording Studio</strong> — document a performance with performers, song, and venue. Optionally log it as a performance at the same time.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Jump to a sub-view</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('night');setTimeout(()=>setNightView('setlist',document.querySelector('#tab-night .tab-sub-btn[onclick*=setlist]')),80);">🎤 Tonight</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('night');setTimeout(()=>setNightView('randomiser',document.querySelector('#tab-night .tab-sub-btn[onclick*=randomiser]')),80);">🎲 Randomizer</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('night');setTimeout(()=>setNightView('requests',document.querySelector('#tab-night .tab-sub-btn[onclick*=requests]')),80);">🙋 Requests</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('night');setTimeout(()=>setNightView('smartsearch',document.querySelector('#tab-night .tab-sub-btn[onclick*=smartsearch]')),80);">🔍 AI Search</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('night');setTimeout(()=>setNightView('studio',document.querySelector('#tab-night .tab-sub-btn[onclick*=studio]')),80);">🎬 Recording Studio</button>
          </div>
        </div>
      </div>

      <div id="help-setlists" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">📋</span><span class="help-acc-title">Setlists</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">Create curated song lists for specific events or performances.</div>
            <div style="margin-bottom:5px;">📋 <strong style="color:var(--text);">Setlist builder</strong> — in Sing → Log Performances. Build a new setlist or load a saved one, then tap ＋ Add to open a Song Picker — a filtered view of your Songbook from which you can add songs one by one without leaving the builder.</div>
            <div style="margin-bottom:5px;">↕️ <strong style="color:var(--text);">Reorder</strong> — drag the ≡ handle on the right of any setlist row to move the song up or down.</div>
            <div style="margin-bottom:5px;">💾 <strong style="color:var(--text);">Setlists are saved</strong> in your backup JSON export and sync to the cloud if signed in.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Try it now</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('night');setTimeout(()=>setNightView('setlist',document.querySelector('#tab-night .tab-sub-btn[onclick*=setlist]')),80);">📋 Build a Setlist</button>
          </div>
        </div>
      </div>

      <div id="help-recording" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">🎬</span><span class="help-acc-title">Recording Features</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <p style="font-size:13px;color:var(--text2);line-height:1.6;margin-bottom:10px;">Record voice memos and identify songs from the floating <strong style="color:var(--text);">🎤 mic button</strong>. (The pitch-reference keyboard is now a <strong style="color:var(--text);">🎹 Pitch Keyboard</strong> button inside <strong style="color:var(--text);">Vox Gym</strong> — no longer a floating button.)</p>
          <div style="background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:10px;">
            <div style="font-size:12px;font-weight:700;color:#ef4444;margin-bottom:4px;">🔴 Voice / REC — Vocal Memo</div>
            <div style="font-size:12px;color:var(--text2);line-height:1.7;">
              Tap the red Voice/REC button to start recording. A persistent toast confirms the recording is active. Tap the pulsing mic icon to stop.<br>
              • Recordings save to your device <em>instantly</em> — no sign-in needed.<br>
              • Sign in to sync memos to the cloud automatically.<br>
              • View all memos in <strong>Vox Gym → My Voice → Voice Notes</strong>. ☁️ = synced, 📱 = device-only.
            </div>
          </div>
          <div style="background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;">
            <div style="font-size:12px;font-weight:700;color:#0891b2;margin-bottom:4px;">🔵 Song / ID — Ambient Song ID</div>
            <div style="font-size:12px;color:var(--text2);line-height:1.7;">
              Tap the cyan Song/ID button. The app listens for ~10 seconds (the main mic icon pulses cyan while active). If a match is found, you'll be prompted to add the song to your Songbook.
            </div>
          </div>
        </div>
      </div>

      <div id="help-venues" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">📍</span><span class="help-acc-title">Venues</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;color:var(--text3);font-style:italic;">BAR's <strong style="color:var(--text2);">featured-region</strong> system currently has hand-curated venues for the <strong style="color:var(--text2);">DC / Maryland / Virginia</strong> area only. Living elsewhere? <em>My Venues</em> and <em>AI Find</em> work in any city. Set your city in <em>Settings → 🎤 Profile</em> so we can scope future regions to you.</div>
            <div style="margin-bottom:5px;">⭐ <strong style="color:var(--text);">My Venues</strong> — your starred venues plus any you've added manually. Same Day / Area / Access filters as Browse, independent state. Works anywhere.</div>
            <div style="margin-bottom:5px;">📍 <strong style="color:var(--text);">Featured</strong> — paged list of the curated featured-region karaoke and open-mic venues with day, area, and accessibility filters. Tap the ☆ on any card to save to <em>My Venues</em>.</div>
            <div style="margin-bottom:5px;">🔍 <strong style="color:var(--text);">AI Find</strong> — describe what you want (city included) and the AI suggests venues that might fit. Works in any region.</div>
            <div style="margin-bottom:14px;">💡 <strong style="color:var(--text);">Suggest an Edit</strong> — every featured venue has a "Suggest an Edit" link inside its Community Notes section. Submissions queue to BAR admin for review.</div>

            <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:6px;">✨ Featured Venue Hubs</div>
            <div style="font-size:12px;color:var(--text2);line-height:1.7;margin-bottom:6px;">Select venues in the featured region have full hub pages that you reach by tapping the card on Featured or My Venues. Each hub URL is shareable (e.g. <code style="font-size:11px;background:var(--surface3);padding:1px 5px;border-radius:4px;">?venue=big-tonys</code>).</div>
            <div style="margin-bottom:5px;">📍 <strong style="color:var(--text);">Check In</strong> — the prominent CTA at the top of every hub. Tapping it sets the venue as your Tonight venue (so songs you log auto-attribute) and tracks your visit. Tap again to Check Out. Visits sync across devices.</div>
            <div style="margin-bottom:5px;">🎤 <strong style="color:var(--text);">Tonight's Setlist</strong> — one-tap jump into the Sing tab's setlist builder with this venue pre-selected.</div>
            <div style="margin-bottom:5px;">💡 <strong style="color:var(--text);">What to Sing Tonight</strong> — four recommendation modes: <em>⭐ Your go-tos</em> (top songs you've sung here, weighted by recency), <em>🎲 Fresh material</em> (proven repertoire you haven't sung here yet), <em>🔥 Crowd-tested</em> (popular friend-aggregated songs that intersect your library), <em>📊 Crowd Hot</em> (top friend-aggregated songs at this venue regardless of your library). One-tap 🎤 Log on any pick.</div>
            <div style="margin-bottom:5px;">📅 <strong style="color:var(--text);">Your History Here</strong> — two stat tiles: Check-ins and Songs Logged (unique / total). Tap either tile to open a modal with the full detail (every visit with timestamps + duration, or every performance with self-rating badges).</div>
            <div style="margin-bottom:5px;">🤝 <strong style="color:var(--text);">Community Notes</strong> — curated public notes about the venue (KJ, vibe, system, quirks). Tap "💡 Suggest an Edit" to submit additions/corrections.</div>
            <div style="margin-bottom:5px;">🗒️ <strong style="color:var(--text);">Your Private Notes</strong> — a free-text scratch pad <em>only you see</em>. Sync across devices. Different from Community Notes (public, moderated).</div>
            <div style="margin-bottom:5px;">🏠 <strong style="color:var(--text);">Hub shortcut from Perform</strong> — whenever your Tonight venue is a featured venue, a small "🏠 Open [Venue] hub" link appears under the venue dropdown so you can jump back to the hub mid-night.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Jump to a sub-view</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('venues');setTimeout(()=>setVenueView('discover',document.getElementById('venue-sub-discover')),80);">📍 Discover</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('venues');setTimeout(()=>setVenueView('mine',document.getElementById('venue-sub-mine')),80);">⭐ My Venues</button>
          </div>
        </div>
      </div>

      <div id="help-voxgym" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">🎙️</span><span class="help-acc-title">Vox Gym</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">🏗 <strong style="color:var(--text);">Foundations</strong> — core vocal principles covering breath support, resonance, registration, articulation, and the speaking-to-singing connection.</div>
            <div style="margin-bottom:5px;">🫁 <strong style="color:var(--text);">Anatomy</strong> — learn how the voice works from the inside: phonation, resonators, registers, and nervous system basics.</div>
            <div style="margin-bottom:5px;">📖 <strong style="color:var(--text);">Technique</strong> — color-coded reference for all 36 vocal techniques across Register, Resonance, Texture, and Phrasing. Tap any card for a deep-dive.</div>
            <div style="margin-bottom:5px;">🎹 <strong style="color:var(--text);">Practice</strong> — structured exercises in five sections: <em>Warm-Up</em> (quick and full routines with honest notes on the science), <em>Fault Correction</em> (fix common problems like breathiness or register breaks), <em>Vocal Foundations</em> (core building-block exercises), <em>Technique Builder</em> (targeted drills for each of the 32 techniques), and a <em>Reading</em> list of recommended books and systems.</div>
            <div style="margin-bottom:5px;">🎙️ <strong style="color:var(--text);">Famous Voices</strong> — browse 820+ artist voice profiles with technique tags, vocal range, and style notes. Tap any profile to see how your songs relate.</div>
            <div style="margin-bottom:5px;">🗂 <strong style="color:var(--text);">My Voice</strong> — 🔒 paid tier: a guided interview using the pitch keyboard to build your personal voice profile — range, peak register, and signature techniques.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Try it now</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('voxgym');">🎙️ Open Vox Gym</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('voxgym');setTimeout(()=>showVgPage('practice'),80);">🎹 Practice</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('voxgym');setTimeout(()=>showVgPage('profiles'),80);">🎙️ Famous Voices</button>
          </div>
        </div>
      </div>

      <div class="help-category-title">Stats &amp; Social</div>

      <div id="help-stats" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">📊</span><span class="help-acc-title">Stats</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">📅 <strong style="color:var(--text);">Performances</strong> — log each time you sing a song and track your history per song.</div>
            <div style="margin-bottom:5px;">🌟 <strong style="color:var(--text);">Self-Rating</strong> — tap the small Rate pill on any row in the Performance Log to cycle through 💪 Crushed it / ✓ Solid / 😬 Off-night. Always private to you. Drives the new <em>Stats → Performance → 🌟 Ratings</em> view (rating distribution, your highest-rated songs, workshop candidates).</div>
            <div style="margin-bottom:5px;">🏆 <strong style="color:var(--text);">Awards</strong> — unlock badges for milestones (10 songs added, first night logged, 50 performances, and more).</div>
            <div style="margin-bottom:5px;">📈 <strong style="color:var(--text);">Stats</strong> — view your overall song count, favorite songs, recent activity, performance trends, and rating breakdowns.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Jump to a sub-view</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');window._statsLandView='perf';showTab('stats');">📊 Performance</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');window._statsLandView='songbook';showTab('stats');">📚 Songbook Stats</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');window._statsLandView='voice';showTab('stats');">🎙️ Voice Stats</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');window._statsLandView='trophy';showTab('stats');">🏆 Trophies</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');window._statsLandView='perflog';showTab('stats');">📋 Performance Log</button>
          </div>
        </div>
      </div>

      <div id="help-people" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">👥</span><span class="help-acc-title">Friends</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">👥 <strong style="color:var(--text);">Friends tab</strong> — manage BAR friends, messages, and private Napkin Notes. A red badge shows unread messages + pending requests.</div>
            <div style="margin-bottom:5px;">➕ <strong style="color:var(--text);">Add a friend</strong> — tap <em>➕ Add New</em>, search anyone by BAR name, and send a request — they approve it on their end (nothing's shared until they do, and you can block or remove anyone anytime). Not on BAR yet? Use <em>Invite to BAR</em> to share your QR or link — they sign up and your request is waiting.</div>
            <div style="margin-bottom:5px;">🪪 <strong style="color:var(--text);">BAR name rules</strong> — at least 3 visible characters (emoji count as 1 each), case-insensitive globally unique, max 24 characters. You can change your BAR name later from <em>Settings → 🎤 Profile</em>, but only <strong>1 change per 30 days</strong> and <strong>5 changes total</strong> for the life of your account. The signup-time set doesn't count toward the 5 — only post-signup renames do.</div>
            <div style="margin-bottom:5px;">📝 <strong style="color:var(--text);">Napkin Notes</strong> — a private local scratch pad for people you meet at shows who aren't on BAR yet. No account needed, never shared.</div>
            <div style="margin-bottom:5px;">🔐 <strong style="color:var(--text);">Sharing prefs</strong> — accepting a request is <em>one tap</em>, and nothing is shared by default. Tap the <strong style="color:var(--text);">🔐</strong> button on any friend card to choose what they can see: Repertoire, Workshop, Stats, Trophies, Performance Log, and whether they can send Song Requests or Instant Messages. Friends only see your BAR name and avatar emoji until you opt in.</div>
            <div style="margin-bottom:5px;">🔒 <strong style="color:var(--text);">Public profile fields</strong> — Signature Song, City, and Bio are <em>opt-in</em> too. Set them in <em>Settings → 🎤 Profile</em> and use the 👁 Preview button to see exactly what friends will see.</div>
            <div style="margin-bottom:5px;">👁 <strong style="color:var(--text);">View songbook</strong> — if a friend shared their Repertoire or Workshop, tap their <em>friend card</em> to open their detail page and browse their songs read-only. Each song card uses the same drawer pattern as AI Search: tap the ◀ handle on the right edge to reveal the actions. The drawer holds <strong style="color:#a78bfa;">📩</strong> (request they perform this song), and when the song is also in <em>your</em> library, <strong style="color:#f472b6;">🎭</strong> (suggest a duet on it). Songs you don't have yet get the <strong style="color:var(--success);">+</strong> / <strong style="color:var(--warning);">+</strong> add-to-library buttons too. The title row carries a <span style="display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;background:var(--success);color:#0a0a0f;font-size:9px;font-weight:700;vertical-align:middle;">✓</span> when you also have the song.</div>
            <div style="margin-bottom:5px;">📩 <strong style="color:var(--text);">Incoming requests</strong> — song suggestions from friends arrive in <strong>Sing → 🙋 Requests</strong>. Accept to add to Workshop, or dismiss. The 📋 Log keeps a full history.</div>
            <div style="margin-bottom:5px;">💬 <strong style="color:var(--text);">Instant messages</strong> — tap <em>💬 IM</em> on a friend card to open a chat thread. Both sides must allow messages in their sharing prefs. Unread count badge shown per friend.</div>
            <div style="margin-bottom:5px;">🔔 <strong style="color:var(--text);">Push notifications</strong> — friend requests and new messages trigger a push notification even when the app is closed. On mobile, install BAR to your home screen first. Toggle message notifications in <em>Settings → 🔒 Account</em>.</div>
            <div style="margin-bottom:5px;">👤 <strong style="color:var(--text);">Profile</strong> — tap any friend's name or avatar to see their public profile. Tap the 👤 header icon → <em>View My Profile</em> to preview your own.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Jump to a sub-view</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('people');setTimeout(()=>setPeopleView('addfriend',document.getElementById('people-sub-addfriend')),80);">➕ Add a Friend</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('people');setTimeout(()=>setPeopleView('friends',document.getElementById('people-sub-friends')),80);">👥 Friends</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('people');setTimeout(()=>setPeopleView('messages',document.getElementById('people-sub-friends')),80);">💬 Messages</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('settings');setTimeout(()=>showSettingsPage('sp-profile',document.querySelector('[onclick*=sp-profile]')),80);">🎤 Edit My Profile</button>
          </div>
        </div>
      </div>

      <div id="help-filters" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">🏷️</span><span class="help-acc-title">Filter Guide</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:10px;">The filter drawer appears throughout BAR. In the Songbook and Song Picker it shows as <strong style="color:var(--text);">＋ Tag Filters</strong> (narrows what you see). In AI Search and the Randomizer it shows as <strong style="color:var(--text);">＋ Tag Restrictions</strong> (constrains what gets picked/generated). Each category has a <strong style="color:var(--text);">?</strong> button that shows a quick reference inline.</div>

          <!-- OR vs AND -->
          <div style="background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:12px;">
            <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:4px;">OR vs. AND Logic</div>
            <div style="font-size:12px;color:var(--text2);line-height:1.6;">The <strong style="color:var(--text);">Next Tag Logic</strong> toggle at the top of the filter drawer controls how multiple tags combine. <strong style="color:var(--text);">OR</strong> returns songs matching <em>any</em> selected tag. <strong style="color:#378ADD;">AND</strong> requires a song to match <em>every</em> selected tag. The mode applies to the next tag you add — existing tags keep the mode they were added with.</div>
          </div>

          <!-- Active Filter Chain (persistent summary outside the drawer) -->
          <div style="background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:12px;">
            <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:4px;">Active Filter Chain</div>
            <div style="font-size:12px;color:var(--text2);line-height:1.6;">When filters are active, the full chain stays visible above the song list — even after you close the drawer. It shows the chips joined by their OR/AND operators, with parentheses around OR-groups (e.g. <em>(Hard Rock OR Pop) AND Showstopper</em>). Tap any chip in the chain to remove just that filter; tap the <strong style="color:var(--text);">✕</strong> at the right end to clear them all.</div>
          </div>

          <!-- Favorites & KaraFun direct toggles -->
          <div style="background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:12px;">
            <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:4px;">⭐ Favorites &amp; 🎤 KaraFun</div>
            <div style="font-size:12px;color:var(--text2);line-height:1.6;">Above the category drawers, two direct toggles narrow your results further: <strong style="color:#f59e0b;">⭐ Favorites</strong> restricts to songs you've starred, and <strong style="color:var(--accent2);">🎤 KaraFun</strong> restricts to songs flagged as available in the KaraFun catalog. Both compose with everything else in your active chain.</div>
          </div>

          <!-- Performance Tags -->
          <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:6px;">🎭 Performance Tags</div>
          <div style="font-size:12px;color:var(--text2);line-height:1.6;margin-bottom:4px;">Describe what a song <em>does in a room</em> — its energy, crowd role, and how you'd deploy it in a set. Set manually in the Edit Song modal or auto-tagged by AI during enrichment.</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:12px;">
            <div><span style="color:var(--text);font-weight:600;">Opener</span> — the on-ramp: mid-tempo, familiar, forgiving; the room keeps chatting but taps along (doesn't demand their full attention)</div>
            <div><span style="color:var(--text);font-weight:600;">Slow Ballad</span> — slow throughout, intimate and vulnerable (does NOT build to a peak — that's Power Ballad)</div>
            <div><span style="color:var(--text);font-weight:600;">Power Ballad</span> — starts sparse and stays slow through the first half, THEN erupts to an anthemic peak (the slow base that explodes is the whole point)</div>
            <div><span style="color:var(--text);font-weight:600;">Rock Out</span> — headbang / air-guitar / aggression, driven by distorted guitars and heavy drums</div>
            <div><span style="color:var(--text);font-weight:600;">Fun/Dance</span> — hip-movement / grooving, driven by a bassline, synth, or pop beat (not rock aggression)</div>
            <div><span style="color:var(--text);font-weight:600;">Camp</span> — the guilty-pleasure crowd-pleaser the room loves because it's <em>not</em> cool; cheesy, over-the-top, performed with a wink (Love Shack, Wannabe, Total Eclipse)</div>
            <div><span style="color:var(--text);font-weight:600;">Showstopper</span> — if the singer stops, the room goes quiet; the burden is entirely on the performer (vocal pyrotechnics, iconic peak)</div>
            <div><span style="color:var(--text);font-weight:600;">Deep Cut</span> — lesser-known gem from a recognizable artist; fans love it, casual listeners don't recognize it. <em>User-applied — not auto-tagged</em> (the AI can't reliably judge obscurity).</div>
            <div><span style="color:var(--text);font-weight:600;">Duet/Group</span> — built for two OR more lead vocalists; classic duets AND 3+ group numbers where the crew passes the mic (boy bands, vocal groups)</div>
            <div><span style="color:var(--text);font-weight:600;">Last Call</span> — the send-off: cathartic, communal, late-night weight; would feel jarring sung at 2pm on a sunny afternoon</div>
            <div><span style="color:var(--text);font-weight:600;">Crowd Work</span> — built-in rests for crowd chanting / call-and-response ("Hey!", "Ba-ba-ba", name-checks)</div>
            <div><span style="color:var(--text);font-weight:600;">Anthem</span> — if the singer drops the mic, the crowd carries the chorus at full volume; organic universal singalong</div>
            <div><span style="color:var(--text);font-weight:600;">Big Ending</span> — steady, then a climactic shift in the final 30-45 seconds; the ending itself is the moment (vs. Power Ballad which builds throughout)</div>
          </div>

          <!-- Genre & Era -->
          <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:4px;">🎵 Genre &amp; 📅 Era</div>
          <div style="font-size:12px;color:var(--text2);line-height:1.6;margin-bottom:12px;"><strong style="color:var(--text);">Genre</strong> — musical style, auto-assigned from iTunes data during enrichment (Rock, Pop, R&amp;B, Country, etc.). <strong style="color:var(--text);">Era</strong> — the decade the song was released (50s/60s through 2020s), also auto-assigned from release year. Both can be overridden manually in the Edit Song modal.</div>

          <!-- Theme -->
          <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:6px;">💛 Theme — Lyrical Content</div>
          <div style="font-size:12px;color:var(--text2);line-height:1.6;margin-bottom:4px;">Describes what a song is <em>about</em>, not how it performs. AI-tagged from the song's known lyrical content (no lyric fetch — derived from the song's reputation). Each song gets up to 2 themes. The 10 matrix themes (split outward/inward) inform your BAR sign; the 7 filter-only extras catch the karaoke canon's other big content buckets without affecting your sign.</div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">External — outward / extroverted (matrix)</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:8px;">
            <div><span style="color:#facc15;font-weight:600;">Defiance</span> — empowerment anthems aimed at an antagonist; standing up, refusing, breaking out (I Will Survive, Roar, Titanium)</div>
            <div><span style="color:#facc15;font-weight:600;">Protest</span> — political, social, or institutional rebellion (Killing in the Name, Fortunate Son)</div>
            <div><span style="color:#facc15;font-weight:600;">Celebration</span> — party, joy, group jubilation (I Gotta Feeling, Uptown Funk)</div>
            <div><span style="color:#facc15;font-weight:600;">Swagger</span> — one-sided sexual confidence, bravado, dominance (SexyBack, 7 Rings)</div>
            <div><span style="color:#facc15;font-weight:600;">Hometown</span> — place-pride, communal belonging, regional identity (New York New York, Sweet Home Alabama)</div>
          </div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Internal — inward / introspective (matrix)</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:8px;">
            <div><span style="color:#facc15;font-weight:600;">Heartbreak</span> — loss, grief over a relationship ended (Someone Like You, Nothing Compares 2 U)</div>
            <div><span style="color:#facc15;font-weight:600;">Existential</span> — mortality, meaning, the big questions (Bohemian Rhapsody, Mad World)</div>
            <div><span style="color:#facc15;font-weight:600;">Longing</span> — unrequited yearning, wanting what you can't have (Someone You Loved, Make You Feel My Love)</div>
            <div><span style="color:#facc15;font-weight:600;">Nostalgia</span> — looking back, reflection on time passed (Yesterday, Landslide)</div>
            <div><span style="color:#facc15;font-weight:600;">Vulnerability</span> — emotional self-exposure, mental-health confession (Breathe Me, 1-800-273-8255)</div>
          </div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Filter-only extras (don't affect your BAR sign)</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:8px;">
            <div><span style="color:#facc15;font-weight:600;">Infatuation</span> — new-love rush, falling-in-love, butterflies (Call Me Maybe, Marry You)</div>
            <div><span style="color:#facc15;font-weight:600;">Enduring Love</span> — settled committed love, eternal-promise pledges (Stand By Me, Endless Love, Wonderful Tonight)</div>
            <div><span style="color:#facc15;font-weight:600;">Sensuality</span> — mutual physical chemistry, slow-jam intimacy (Let's Get It On, Earned It)</div>
            <div><span style="color:#facc15;font-weight:600;">Spite</span> — vindictive anger, retribution (Before He Cheats, You Oughta Know)</div>
            <div><span style="color:#facc15;font-weight:600;">Tavern</span> — drinking, the bar, beer (Friends in Low Places, Margaritaville)</div>
            <div><span style="color:#facc15;font-weight:600;">Humor</span> — comedic, satirical, novelty (Detachable Penis, Forget You)</div>
            <div><span style="color:#facc15;font-weight:600;">Faith</span> — religious, spiritual, devotional (Amazing Grace, Oh Happy Day)</div>
          </div>
          <div style="font-size:11px;color:var(--text3);line-height:1.6;margin-bottom:12px;"><strong style="color:#facc15;">Generic/Other</strong> — songs the AI analyzed but didn't fit any of the 17 themes (rare under the expanded vocabulary). Distinct from songs not yet analyzed.</div>

          <!-- Voice Foundation -->
          <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:4px;">🎙️ Voice Foundation — Register &amp; Resonance</div>
          <div style="font-size:12px;color:var(--text2);line-height:1.6;margin-bottom:6px;">Covers three dimensions of vocal filtering: register, resonance, and range. Technique tags are drawn live from the Famous Voices database by artist — no tagging step required. Range chips (Vox ↑↑ etc.) match an artist's FVP range tier, or song-specific AI Vocal Data if available.</div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Register</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:8px;">
            <div><span style="color:var(--text);font-weight:600;">Modal/Speech</span> — normal speaking-voice register, the default</div>
            <div><span style="color:var(--text);font-weight:600;">Full/Head Voice</span> — upper register, full fold closure, not falsetto</div>
            <div><span style="color:var(--text);font-weight:600;">Witch Voice</span> — strained pharyngeal upper register, hard rock "edge"</div>
            <div><span style="color:var(--text);font-weight:600;">Falsetto</span> — light, airy upper register, incomplete fold closure</div>
            <div><span style="color:var(--text);font-weight:600;">Belt</span> — chest voice driven above the break, the power voice</div>
            <div><span style="color:var(--text);font-weight:600;">Whistle</span> — extreme top register above high C, Mariah territory</div>
          </div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Resonance</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:12px;">
            <div><span style="color:var(--text);font-weight:600;">Nasal (Harsh)</span> — buzzy, unpleasant nasal quality</div>
            <div><span style="color:var(--text);font-weight:600;">Nasal (Neutral)</span> — present but not unpleasant nasality</div>
            <div><span style="color:var(--text);font-weight:600;">Nasal (Sweet)</span> — light, pleasant nasal brightness</div>
            <div><span style="color:var(--text);font-weight:600;">Nasal + Chested</span> — nasal resonance with heavy chest weight</div>
            <div><span style="color:var(--text);font-weight:600;">Open Chest</span> — chest-dominant, minimal nasality, grounded</div>
            <div><span style="color:var(--text);font-weight:600;">Twang</span> — bright cutting "ping," projects over loud instruments</div>
            <div><span style="color:var(--text);font-weight:600;">Head</span> — pure floaty head-voice tone, light and ethereal</div>
            <div><span style="color:var(--text);font-weight:600;">Head/Mask</span> — head voice with forward mask, full classical ring</div>
            <div><span style="color:var(--text);font-weight:600;">Mask (Sweet)</span> — bright sweet forward placement, MJ/Bruno Mars</div>
            <div><span style="color:var(--text);font-weight:600;">Mask (Narrow)</span> — mask sweet focused into a tight pointed beam</div>
          </div>

          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Vocal Range</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:12px;">
            <div><span style="color:var(--text);font-weight:600;">Vox ↑↑</span> — above C5, very high range</div>
            <div><span style="color:var(--text);font-weight:600;">Vox ↑</span> — A4–C5, high range</div>
            <div><span style="color:var(--text);font-weight:600;">Vox →</span> — E4–A4, mid range</div>
            <div><span style="color:var(--text);font-weight:600;">Vox ↓</span> — lower range</div>
            <div><span style="color:var(--text);font-weight:600;">Vox ↓↓</span> — below C3, very low range</div>
          </div>

          <!-- Voice Style -->
          <div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:4px;">✨ Voice Style — Texture &amp; Phrasing</div>
          <div style="font-size:12px;color:var(--text2);line-height:1.6;margin-bottom:6px;">Describes the distinctive stylistic flavours layered on top of register — how the voice is colored and how phrases are delivered.</div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Texture</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:8px;">
            <div><span style="color:var(--text);font-weight:600;">Rasp</span> — mild consistent roughness, sustainable sandpaper</div>
            <div><span style="color:var(--text);font-weight:600;">Grit</span> — harder bright distortion with crunch, AC/DC style</div>
            <div><span style="color:var(--text);font-weight:600;">Growl</span> — deep dark rolling rumble, grunge and rock intensity</div>
            <div><span style="color:var(--text);font-weight:600;">Fry</span> — under-driven intimate creak, Billie Eilish style</div>
            <div><span style="color:var(--text);font-weight:600;">Scream</span> — maximum overdrive, pitch partially abandoned</div>
            <div><span style="color:var(--text);font-weight:600;">Compression</span> — pressed-but-clean, every note feels weighted</div>
            <div><span style="color:var(--text);font-weight:600;">Modal Pinch</span> — pharyngeal narrowing in the upper range</div>
            <div><span style="color:var(--text);font-weight:600;">Vibrato (Wide)</span> — slow operatic oscillation, Freddie/Celine</div>
            <div><span style="color:var(--text);font-weight:600;">Vibrato (Fast)</span> — rapid tight oscillation, Stevie/Sam Cooke</div>
            <div><span style="color:var(--text);font-weight:600;">Straight Tone</span> — no vibrato, pure sustained pitch, folk/indie</div>
            <div><span style="color:var(--text);font-weight:600;">Breath / Smoke</span> — deliberate breathiness, soft and smoky</div>
            <div><span style="color:var(--text);font-weight:600;">Cry</span> — emotionally weighted depth, the aching gospel quality</div>
            <div><span style="color:var(--text);font-weight:600;">Covered</span> — dark warm round tone, nasal port closed, no cut</div>
          </div>
          <div style="font-size:11px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:3px;">Phrasing</div>
          <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px;font-size:11px;color:var(--text2);margin-bottom:4px;">
            <div><span style="color:var(--text);font-weight:600;">Speak-Sing</span> — melody and speech blurred, conversational</div>
            <div><span style="color:var(--text);font-weight:600;">Runs &amp; Riffs</span> — rapid melodic ornamentation, gospel flourishes</div>
            <div><span style="color:var(--text);font-weight:600;">Portamento</span> — sliding smoothly between pitches, Elvis/Sinatra</div>
            <div><span style="color:var(--text);font-weight:600;">Grunt</span> — short percussive interjections, James Brown style</div>
            <div><span style="color:var(--text);font-weight:600;">Microtonal</span> — deliberate inflections between notes, micro-bends</div>
            <div><span style="color:var(--text);font-weight:600;">Rapid Fire</span> — high syllable density, rap-adjacent delivery</div>
            <div><span style="color:var(--text);font-weight:600;">Yodel / Break</span> — deliberate audible chest↔head flip, exposed on purpose (Jewel, LeAnn Rimes)</div>
          </div>
        </div>
      </div>

      <div class="help-category-title">Account &amp; App</div>

      <div id="help-sync" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">☁️</span><span class="help-acc-title">Cloud Sync &amp; Account</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">👤 Tap the account icon in the header to <strong style="color:var(--text);">sign in</strong>, create a free account, or quickly access your profile.</div>
            <div style="margin-bottom:5px;">☁️ Once signed in, all data (songs, venues, settings, recordings) <strong style="color:var(--text);">syncs automatically</strong> in the background.</div>
            <div style="margin-bottom:5px;">📱 <strong style="color:var(--text);">Guest migration</strong> — your existing local data is automatically uploaded on first sign-in. Cloud data wins on any conflicts.</div>
            <div style="margin-bottom:5px;">📶 <strong style="color:var(--text);">Offline support</strong> — changes made while offline are queued and synced as soon as you reconnect.</div>
            <div style="margin-bottom:5px;">🔓 <strong style="color:var(--text);">Sign out any time</strong> — your data stays on the device.</div>
            <div style="margin-bottom:5px;">✉️ <strong style="color:var(--text);">Sign in with email and password</strong> — your data stays private and synced across devices. Use <em>Forgot Password</em> to reset via email if needed.</div>
            <div style="margin-bottom:5px;">⚙️ <strong style="color:var(--text);">Full account settings</strong> — go to <em>⚙️ Settings → 🔒 Account</em> to manage notifications, change your password, or delete your account. (Set your BAR name in <em>Settings → 🎤 Profile</em>; back up &amp; restore your data from <em>☰ More → 💾 Your Data</em>.)</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Try it now</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('settings');setTimeout(()=>showSettingsPage('sp-account',document.querySelector('[onclick*=sp-account]')),80);">🔒 Open Account Settings</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');setTimeout(()=>handleAuthHeaderBtn(),80);">👤 Sign In / My Profile</button>
          </div>
        </div>
      </div>

      <div id="help-import" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">📂</span><span class="help-acc-title">Importing &amp; Exporting Data</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">📂 <strong style="color:var(--text);">Data Manager</strong> — go to <em>☰ More → 💾 Your Data</em> to export a full JSON backup. The backup is a comprehensive snapshot: songs + performances, venues + your private notes, setlists, trophies, custom tags, requests log, AI Search/Suggest log, KaraFun catalog, voice memo titles, every UI preference, AND (when signed in) your public profile and friend list. It's a true everything-you-created backup.</div>
            <div style="margin-bottom:5px;">💾 <strong style="color:var(--text);">One place for data</strong> — <em>☰ More → 💾 Your Data</em> keeps backup, restore, snapshot recovery, Diagnose, and Clear All Data together.</div>
            <div style="margin-bottom:5px;">🔁 <strong style="color:var(--text);">Deduplication</strong> — loading the same JSON twice is safe. Songs matched by title + artist are skipped; the toast shows what was added vs. skipped.</div>
            <div style="margin-bottom:5px;">🏷️ <strong style="color:var(--text);">Tags migrate automatically</strong> — tags from imported songs merge into your library without duplicates.</div>
            <div style="margin-bottom:5px;">🔍 <strong style="color:var(--text);">Diagnose Data</strong> — in <em>☰ More → 💾 Your Data</em>. Scans your songs for missing fields, duplicates, or corrupt records.</div>
          </div>
          <div class="help-jump-row">
            <div class="help-jump-label">Try it now</div>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');showTab('songs');setTimeout(openBulkAddSong,80);">📋 Open Bulk Add</button>
            <button class="btn btn-secondary btn-sm" onclick="closeModal('help-modal');setTimeout(()=>{ if (typeof openDataModal === 'function') openDataModal(); else { showTab('settings'); setTimeout(()=>showSettingsPage('sp-account',document.querySelector('[onclick*=sp-account]')),50); } },80);">📦 Backup &amp; Restore</button>
          </div>
        </div>
      </div>

      <div id="help-settings" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">⚙️</span><span class="help-acc-title">Settings</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">⚙️ <strong style="color:var(--text);">Settings = config only.</strong> Tap the ⚙️ header icon — two pages: <strong>🎤 Profile</strong> (BAR name, avatar emoji, signature song, city, bio — each <em>opt-in</em>; only BAR name + avatar emoji show to friends by default; tap 👁 Preview to check) and <strong>🔒 Account</strong> (message &amp; trophy notifications, Screenshot Mode, change password, restore orientation tips, sign out, delete account).</div>
            <div style="margin-bottom:5px;">☰ <strong style="color:var(--text);">Everything else lives in More.</strong> Tap the ☰ header icon for: <strong>Walkthrough</strong> &amp; <strong>Highlight Reel</strong> (quick tours), <strong>Help &amp; Guide</strong> (this reference), <strong>Install BAR as an App</strong>, <strong>💾 Your Data</strong> (backup / restore / diagnose / clear), <strong>🏷️ Tags &amp; Tools</strong>, <strong>About</strong>, and <strong>Report a Bug</strong>.</div>
            <div style="margin-bottom:5px;">🏷️ <strong style="color:var(--text);">Tags &amp; Tools</strong> (in More) — create custom tags, upload a KaraFun catalog CSV, and run Auto-Enrich Songbook to backfill older songs missing metadata, performance tags, or KaraFun availability. New songs auto-enrich at add time, so this is mostly catch-up. Vocal-technique filter tags come live from the Famous Voices database — no tagging step. Per-song AI Vocal Data (range + technique tags) is a paid feature.</div>
            <div style="margin-bottom:5px;">🐛 <strong style="color:var(--text);">Report a Bug</strong> (in More) — opens your email client with device/browser info pre-filled.</div>
          </div>
        </div>
      </div>

      <div id="help-update" class="help-section" style="margin-bottom:6px;">
        <button class="help-acc-head" onclick="toggleHelpAcc(this)" aria-expanded="false">
          <span class="help-acc-icon">↺</span><span class="help-acc-title">Updating the App</span><span class="help-acc-arrow">▸</span>
        </button>
        <div class="help-acc-body" style="display:none;">
          <p style="font-size:13px;color:var(--text2);line-height:1.7;margin-bottom:8px;">Building A Rockstar is a web app hosted on Cloudflare. Updates are pushed automatically — but your browser may cache an old version.</p>
          <div style="font-size:13px;color:var(--text2);line-height:1.8;">
            <div style="margin-bottom:5px;">↺ Tap <strong style="color:var(--text);">BAR v4.99885 ↺</strong> (top right of the header) for an instant hard refresh.</div>
            <div style="margin-bottom:5px;">⚙️ Or go to <strong style="color:var(--text);">☰ More → ℹ️ About</strong> and tap "Reload to get latest version".</div>
            <div style="margin-bottom:5px;">✅ A hard reload bypasses the browser cache. Your song data and settings are <em>never</em> affected.</div>
          </div>
        </div>
      </div>

      <div style="text-align:center;padding-top:4px;border-top:1px solid var(--border);">
        <button class="btn btn-secondary" style="width:100%;justify-content:center;" onclick="closeModal('help-modal')">Close</button>
      </div>
    </div>
  </div>
</div>
<!-- BAR name onboarding gate — no close button, z-index above everything -->
<div class="modal-overlay" id="bar-name-gate-modal" data-locked="1" style="z-index:10001;">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div class="modal-title" style="margin-bottom:6px;">🎤 Choose Your BAR Name</div>
      <p style="font-size:13px;color:var(--text2);margin-bottom:16px;line-height:1.6;">
        Your <strong style="color:var(--text);">BAR Name</strong> is how you'll be known and found on the site — by friends, at venues, everywhere. Make it unique and memorable!<br><br>
        <span style="color:var(--text3);font-size:12px;">Your email address is never visible to other users.</span>
      </p>
      <div class="form-group" style="margin-bottom:6px;">
        <label class="form-label">BAR Name</label>
        <input class="form-input" id="bar-name-gate-input" placeholder="At least 6 characters" maxlength="40"
          oninput="onBARNameGateInput(this.value)">
      </div>
      <div id="bar-name-gate-msg" style="font-size:12px;min-height:18px;margin-bottom:12px;"></div>
      <button id="bar-name-gate-btn" class="btn btn-primary" style="width:100%;justify-content:center;"
        onclick="saveBARNameGate()" disabled>Set My BAR Name</button>
    </div>
  </div>
</div>

<!-- SIZZLE REEL MODAL — the in-app "Highlight Reel" feature tour.
     Slide deck data + render logic live in sizzle-reel.js. Slide images
     are static .jpg files in /images/sizzle/. The modal supports swipe
     navigation, keyboard arrow keys, ESC to close. Some slides have two
     images side-by-side (configured per-slide in SIZZLE_SLIDES); code-gen
     slides (01 cover, 17 cloud sync, 18 closer) are pure HTML. -->
<div class="modal-overlay" id="sizzle-modal" onclick="closeModalOnBg(event,'sizzle-modal')">
  <div class="modal sizzle-modal">
    <div class="sizzle-header">
      <span class="sizzle-progress" id="sizzle-progress">1 / 18</span>
      <button class="sizzle-close" onclick="closeSizzleReel()" aria-label="Close">✕</button>
    </div>
    <div class="sizzle-stage" id="sizzle-stage">
      <!-- slide content rendered into here by renderSizzleSlide() -->
    </div>
    <div class="sizzle-nav">
      <button class="sizzle-nav-btn" id="sizzle-prev" onclick="sizzlePrev()" aria-label="Previous slide">‹</button>
      <div class="sizzle-dots" id="sizzle-dots"></div>
      <button class="sizzle-nav-btn" id="sizzle-next" onclick="sizzleNext()" aria-label="Next slide">›</button>
    </div>
  </div>
</div>

<div class="modal-overlay" id="auth-modal" onclick="closeModalOnBg(event,'auth-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div class="modal-title" id="auth-modal-title" style="margin-bottom:16px;">☁️ Sign In / Sign Up</div>
      <div id="auth-modal-body"></div>
    </div>
  </div>
</div>

<!-- PROFILE MODAL — public-facing profile shown when tapping a friend's name/avatar, or your own from Account -->
<div class="modal-overlay" id="profile-modal" onclick="closeModalOnBg(event,'profile-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div id="profile-modal-body"></div>
    </div>
  </div>
</div>

<!-- MESSAGES SETTINGS MODAL — privacy toggles for outgoing chat behaviour. -->
<div class="modal-overlay" id="msg-settings-modal" onclick="closeModalOnBg(event,'msg-settings-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div style="padding:8px 4px 16px;">
        <div style="font-family:'Bebas Neue',sans-serif;font-size:22px;letter-spacing:1px;color:var(--text);margin-bottom:6px;">⚙ Messages Settings</div>
        <div style="font-size:12px;color:var(--text3);line-height:1.6;margin-bottom:18px;">
          These toggles control what <strong style="color:var(--text2);">you send out</strong>. You'll always see what friends share with you regardless of these — each person controls their own.
        </div>

        <!-- Browser notifications -->
        <div style="display:flex;align-items:flex-start;gap:12px;padding:10px 0;border-bottom:1px solid var(--border);">
          <div style="flex:1;min-width:0;">
            <div style="font-size:13px;font-weight:600;color:var(--text);">🔔 Browser notifications</div>
            <div style="font-size:11px;color:var(--text3);margin-top:2px;line-height:1.5;">Get a system notification when a new message arrives.</div>
          </div>
          <div class="ios-toggle-track" id="ms-toggle-notifs" onclick="toggleMsgSettingNotifs()" style="cursor:pointer;flex-shrink:0;margin-top:2px;"><div class="ios-toggle-knob"></div></div>
        </div>

        <!-- Read receipts -->
        <div style="display:flex;align-items:flex-start;gap:12px;padding:10px 0;border-bottom:1px solid var(--border);">
          <div style="flex:1;min-width:0;">
            <div style="font-size:13px;font-weight:600;color:var(--text);">✓✓ Read receipts</div>
            <div style="font-size:11px;color:var(--text3);margin-top:2px;line-height:1.5;">Let friends see when you've read their messages. When off, they'll only see <strong style="color:var(--text2);">✓</strong> (delivered).</div>
          </div>
          <div class="ios-toggle-track" id="ms-toggle-read-receipts" onclick="toggleMsgSettingReadReceipts()" style="cursor:pointer;flex-shrink:0;margin-top:2px;"><div class="ios-toggle-knob"></div></div>
        </div>

        <!-- Typing indicators -->
        <div style="display:flex;align-items:flex-start;gap:12px;padding:10px 0;border-bottom:1px solid var(--border);">
          <div style="flex:1;min-width:0;">
            <div style="font-size:13px;font-weight:600;color:var(--text);">••• Typing indicator</div>
            <div style="font-size:11px;color:var(--text3);margin-top:2px;line-height:1.5;">Let friends see "is typing…" while you're composing.</div>
          </div>
          <div class="ios-toggle-track" id="ms-toggle-typing" onclick="toggleMsgSettingTyping()" style="cursor:pointer;flex-shrink:0;margin-top:2px;"><div class="ios-toggle-knob"></div></div>
        </div>

        <!-- Notification preview -->
        <div style="display:flex;align-items:flex-start;gap:12px;padding:10px 0;">
          <div style="flex:1;min-width:0;">
            <div style="font-size:13px;font-weight:600;color:var(--text);">📱 Show preview in notifications</div>
            <div style="font-size:11px;color:var(--text3);margin-top:2px;line-height:1.5;">Show the actual message text in system notifications. Off shows a generic "📩 New message" instead, so glances at your phone don't reveal content.</div>
          </div>
          <div class="ios-toggle-track" id="ms-toggle-preview" onclick="toggleMsgSettingPreview()" style="cursor:pointer;flex-shrink:0;margin-top:2px;"><div class="ios-toggle-knob"></div></div>
        </div>

        <div style="text-align:center;margin-top:18px;">
          <button class="btn btn-secondary btn-sm" onclick="closeMsgSettings()">Done</button>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- VENUE STAT DETAIL MODAL — used by the venue hub "Your History Here" stat tiles -->
<div class="modal-overlay" id="venue-stat-modal" onclick="closeModalOnBg(event,'venue-stat-modal')">
  <div class="modal">
    <div class="modal-scroll-body">
      <div class="modal-handle"></div>
      <div id="venue-stat-modal-body"></div>
    </div>
  </div>
</div>

<!-- B: MY QR CODE MODAL (QR invite) -->
<div class="modal-overlay" id="my-qr-modal" onclick="if(event.target===this)closeModal('my-qr-modal')">
  <div class="modal-box" style="text-align:center;padding:24px 20px;max-width:300px;">
    <div style="font-size:15px;font-weight:700;margin-bottom:4px;">📲 Your Invite QR</div>
    <div style="font-size:12px;color:var(--text3);margin-bottom:14px;">Share with someone not on BAR yet — they scan, sign up, and a friend request from you is waiting. Already-BAR friends? Just have them search your BAR name. Link valid for 24 hours.</div>
    <div style="background:#fff;border-radius:10px;padding:12px;display:inline-block;min-width:200px;min-height:200px;">
      <img id="my-qr-img" style="width:200px;height:200px;display:block;" alt="QR code">
    </div>
    <div id="my-qr-name" style="margin-top:10px;font-size:14px;font-weight:700;color:var(--text);"></div>
    <span id="my-qr-link" style="display:none;"></span>
    <div style="display:flex;gap:8px;margin-top:14px;">
      <button class="btn btn-secondary" style="flex:1;" onclick="_copyQRLink('my-qr-link',this)">🔗 Copy link</button>
      <button class="btn btn-secondary" style="flex:1;" onclick="closeModal('my-qr-modal')">Close</button>
    </div>
  </div>
</div>

<!-- B: INVITE WELCOME MODAL (non-BAR user scanned a QR, not yet signed in) -->
<div class="modal-overlay" id="bar-invite-welcome-modal">
  <div class="modal-box" style="padding:24px 20px;max-width:300px;">
    <div style="font-size:15px;font-weight:700;margin-bottom:8px;">📲 You've been invited!</div>
    <div id="bar-invite-welcome-msg" style="font-size:13px;color:var(--text2);margin-bottom:14px;line-height:1.5;"></div>
    <div style="font-size:11px;color:var(--text3);background:var(--surface2);border-radius:var(--radius-sm);padding:10px 12px;margin-bottom:18px;line-height:1.5;">💡 Pro-tip: Bookmark or screenshot this page before signing up — if anything goes wrong you can navigate back to it.</div>
    <div style="display:flex;gap:8px;">
      <button class="btn btn-secondary" style="flex:1;" onclick="closeModal('bar-invite-welcome-modal')">Maybe Later</button>
      <button class="btn btn-primary" style="flex:1;" onclick="closeModal('bar-invite-welcome-modal');_authView='signup';renderAuthModal();openModal('auth-modal')">Sign Up</button>
    </div>
  </div>
</div>

<!-- B: INVITE CONFIRM MODAL (BAR user scanned someone's invite QR) -->
<div class="modal-overlay" id="bar-invite-modal">
  <div class="modal-box" style="padding:24px 20px;max-width:300px;">
    <div style="font-size:15px;font-weight:700;margin-bottom:8px;">📲 Invite QR</div>
    <div id="bar-invite-msg" style="font-size:13px;color:var(--text2);margin-bottom:18px;line-height:1.5;"></div>
    <div style="display:flex;gap:8px;">
      <button class="btn btn-secondary" style="flex:1;" onclick="closeModal('bar-invite-modal')">Cancel</button>
      <button class="btn btn-primary" style="flex:1;" onclick="_confirmBarInvite()">Send Request</button>
    </div>
  </div>
</div>

<!-- 2026-06-06 — the 3 QR Handshake modals were removed with the friend-connect
     simplification (handshake retired in favor of search → instant request →
     approve). Invite-QR modals above (#my-qr-modal / #bar-invite-* ) stay. -->

<!-- NIGHT-IN-REVIEW (2026-06-06, Tonight board Phase 3) — the "morning after"
     batch reconciliation. Fired by _maybeNightInReview() in setlist.js when a
     venue session ended unreviewed; body filled by renderNightReview(). Tap any
     songs you sang but forgot to log → batch-logged, dated at the session time. -->
<div class="modal-overlay" id="night-review-modal" onclick="if(event.target===this)_nightReviewDismiss()">
  <div class="modal-box" style="padding:22px 18px;max-width:340px;">
    <div id="night-review-body"></div>
  </div>
</div>

<!-- A–Z SHEET (2026-06-06, Tonight board Phase 4) — type-free browse of the whole
     repertoire (alpha by title) with a letter rail. Body filled by renderAzSheet()
     in setlist.js; each row taps to log. Opened via openAzSheet() from the board. -->
<!-- The standalone A–Z sheet (#az-sheet-modal) was retired 2026-06-08 — its A–Z
     grouping + jump rail folded into the combined Browse & Pick sheet
     (#picker-modal), which now also carries tag filters, search, and dual
     ⚡ Log / ＋ Backstage actions. openAzSheet() is a thin alias → openSongPicker. -->

<!-- FRIEND MANAGE (2026-06-06, whole-card-tap refactor) — sharing prefs + contact
     options + Unfriend/Block, moved OFF the friend card (which is now a single
     tap-target → friend detail page). Opened via the 🔐 button on the friend
     detail-page header. Body filled by openFriendManage(fId) in social.js. -->
<div class="modal-overlay" id="friend-manage-modal" onclick="if(event.target===this)closeModal('friend-manage-modal')">
  <div class="modal-box" style="padding:20px 18px;max-width:360px;width:92%;">
    <div id="friend-manage-body"></div>
  </div>
</div>

<script src="social.js"></script>
<script src="sync.js"></script>
<script src="auth.js"></script>
</body>
</html>

