ZodiacZ flavors your profile, your VibeZ matches, and your BattleZ commentary.
` : `
Add your birthday to unlock ZodiacZ. It auto-detects your sign and uses it across matching, profile flavor, and BattleZ commentary โ no manual selection needed.
`; showModal('ZodiacZ', z); } function saveZodiacBday() { const v = document.getElementById('zodiacBdayInput').value; if (!v) return; state.user.birthday = v; applyZodiacFromBirthday(); closeModal(); // Refresh current page so any zodiac-aware content updates route(currentRoute()); } /* ======================================================================== TAB / ROUTE DEFINITIONS This is the single source of truth for the top-level tabs and their sub-apps. URL hash is /tabkey or /tabkey/subkey. ======================================================================== */ const ICON = (file, fallbackEmoji) => ({ // 'file' is relative path to icon image; we let CSS background-image take it // and fall back to an emoji if the file is missing in deployment. file, fallback: fallbackEmoji }); const TABS = [ { key: 'home', label: 'Home', emoji: '๐ ', title: '๐ Home', corey: "Yo โ this is your home base. Personas, skills, profile pic, ZodiacZ. Set this up first and the whole app gets smarter about you.", subapps: [] }, { key: 'collabz', label: 'CollabZ', emoji: '๐ค', title: '๐ค CollabZ โ Collaborate with other users', corey: "CollabZ is where you actually work with people. Covers, remixes, projects. You make it, somebody else flips it. Or you flip theirs.", subapps: [ { key: 'coverz', name: 'CoverZ', emoji: '๐ซด๐ผ', desc: "Covers of songs, redraws of existing art, performing existing song covers. Tribute mode.", icon: ICON(null, '๐ซด๐ผ') }, { key: 'remixez', name: 'RemixeZ', emoji: '๐', desc: "Remix posts and songz. Take someone's PostZ and flip it into your version.", icon: ICON(null, '๐') } ] }, { key: 'battlez', label: 'BattleZ', emoji: '๐ช', title: '๐ช BattleZ โ Post vs Post', corey: "One post vs another. Contestants verified 18+ can bet real money on themselves. Everyone else throws SpinaZ. Winner takes the pot, loser takes the lesson.", subapps: [ { key: 'freestyle', name: 'Freestyle', emoji: '๐', desc: "Live, sporadic battles. Open mic, no notice, prove it in the moment.", icon: ICON(null, '๐') }, { key: '1v1', name: '1v1', emoji: '1โฃ', desc: "One artist vs one artist. Other personas helping doesn't change the count โ it's still 1v1.", icon: ICON(null, '1โฃ') }, { key: 'cypher', name: 'Battle Cypher', emoji: '๐งโ๐คโ๐ง', desc: "More than one artist vs more than one artist. Personas helping doesn't change the count.", icon: ICON(null, '๐งโ๐คโ๐ง') } ] }, { key: 'social-connectz', label: 'Social', emoji: '๐', title: '๐ Social ConnectZ', corey: "Match for romance or collab โ and we make you say which. No more 'oh I thought this was professional.' Pick a lane.", subapps: [ { key: 'vibez', name: 'VibeZ', emoji: 'โฅ๏ธ', desc: "Plenty-of-Fish style. Advanced match for romance or collab โ you mark which up front.", icon: ICON('assets/icons/vibez.jpg', 'โฅ๏ธ') }, { key: 'inferno', name: 'Inferno', emoji: 'โค๏ธโ๐ฅ', desc: "Tinder style. Fast swipe match for romance or quick collab. Intent flagged on every profile.", icon: ICON('assets/icons/inferno.jpg', 'โค๏ธโ๐ฅ') }, { key: 'boardz', name: 'BoardZ', emoji: '๐ชง', desc: "Old-school message boards. Threaded talk, no algorithm.", icon: ICON(null, '๐ชง') }, { key: 'personalitiez', name: 'PersonalitieZ', emoji: '๐ถ', desc: "MBTI tests, simple and complex. Feeds into VibeZ + Inferno matching.", icon: ICON(null, '๐ถ') }, { key: 'preferencez', name: 'PreferenceZ', emoji: '๐ป', desc: "Partner genderZ + compatibility filters. Powers who shows up in VibeZ and Inferno.", icon: ICON('assets/icons/preferencez.jpg', '๐ป') } ] }, { key: 'messagez', label: 'MessageZ', emoji: '๐จ', title: '๐จ MessageZ', corey: "Inbox + outbox. That's it. No clever feed, no algorithmic 'priority' โ your messages, in order, sent and received.", subapps: [ { key: 'inbox', name: 'Inbox', emoji: '๐ฅ', desc: "Messages people sent you.", icon: ICON(null, '๐ฅ') }, { key: 'outbox', name: 'Outbox', emoji: '๐ค', desc: "Messages you sent.", icon: ICON(null, '๐ค') } ] }, { key: 'callz', label: 'CallZ', emoji: '๐', title: '๐ CallZ โ Voice & AI calls', corey: "Calls cost money. Available to StatZ-tier users. SpinaZ won't cut it here โ try, you'll get 'coming soon' and a prompt to load real balance via Stripe/PayPal.", subapps: [ { key: 'ai', name: 'AI CallZ', emoji: '๐ค', desc: "Talk to AI. Cost depends on the model you pick.", icon: ICON('assets/icons/ai-call.png', '๐ค') }, { key: 'user', name: 'User CallZ', emoji: '๐ฃ๏ธ', desc: "Talk to other users. Cost = that user's hourly skill rate.", icon: ICON('assets/icons/user-callz.webp', '๐ฃ๏ธ') } ] }, { key: 'bugz', label: 'BugZ', emoji: '๐', title: '๐ BugZ โ Report it, get paid for it', corey: "Found a bug? Post it. Admin marks it 'In Progress' or 'Squashed'. Squashed = 200 SpinaZ in your account. Quality bug reports are basically a side hustle.", subapps: [ { key: 'submit', name: 'Submit Bug', emoji: '๐', desc: "Submit a bug as a post. Include steps to reproduce.", icon: ICON('assets/icons/bugz.jpg', '๐') }, { key: 'feed', name: 'Bug Feed', emoji: '๐', desc: "All posted bugs and their status: ๐ญ In Progress, ๐ชฆ Squashed.", icon: ICON(null, '๐') } ] }, { key: 'distributez', label: 'DistributeZ', emoji: '๐ถ', title: '๐ถ DistributeZ โ Submit for distribution', corey: "Audio becomes the track. Image becomes the cover. Text becomes lyrics. Album posts become album submissions. Free users get 1 a month โ Premium and StatZ get unlimited and StatZ can submit for licensing.", subapps: [ { key: 'single', name: 'Submit Single', emoji: '๐ต', desc: "Submit one PostZ as a single distribution.", icon: ICON(null, '๐ต') }, { key: 'album', name: 'Submit Album', emoji: '๐ฟ', desc: "Album posts populate as album submissions. Editable before send.", icon: ICON(null, '๐ฟ') }, { key: 'licensing', name: 'Licensing (StatZ)', emoji: 'โ๏ธ', desc: "StatZ only โ submit for sync and licensing deals.", icon: ICON(null, 'โ๏ธ') } ] }, { key: 'royaltiez', label: 'RoyaltieZ', emoji: '๐', title: '๐ RoyaltieZ โ Balance & logz', corey: "Your money. Where it came from. When it landed. Timestamped, no hand-waving.", subapps: [] }, { key: 'groupz', label: 'GroupZ', emoji: '๐ฅ', title: '๐ฅ GroupZ โ Your private circles', corey: "GroupZ are local to you โ you decide who's a friend, partner, fan, blocked. Custom groups too. Nobody else sees your labels.", subapps: [ { key: 'friends', name: 'Friends', emoji: '๐', desc: "Mutual benefit relationships.", icon: ICON(null, '๐') }, { key: 'fans', name: 'Fans', emoji: '๐๐ฝ', desc: "Less need from you, more from them.", icon: ICON(null, '๐๐ฝ') }, { key: 'partners', name: 'Partners', emoji: '๐๐ฝ', desc: "People you work with frequently.", icon: ICON(null, '๐๐ฝ') }, { key: 'blocked', name: 'Blocked', emoji: '๐ค๐ฝ', desc: "Cannot contact you.", icon: ICON(null, '๐ค๐ฝ') }, { key: 'custom', name: 'Custom', emoji: 'โ', desc: "Make your own labels.", icon: ICON(null, 'โ') } ] }, { key: 'labelz', label: 'LabelZ', emoji: '๐ท๏ธ', title: '๐ท๏ธ LabelZ โ Public label-style groups', corey: "Public GroupZ with teeth. Premium A&R Scout or Manager persona required to create or edit. Real label flow: advances, terms, e-signed contracts.", subapps: [] }, { key: 'toolz', label: 'ToolZ', emoji: '๐ก', title: '๐ก ToolZ โ Build, write, draw, edit, sing, rap, train', corey: "Everything that opens media also lives here. Every tool has its own URL and its own modal. Text, image, video, plus the Lilith/SingZ/RapZ/BodieZ practice apps.", subapps: [ { key: 'keyconnectz', name: 'Key ConnectZ', emoji: 'โจ๏ธ', desc: "System keyboard on mobile, floating keyboard on desktop. Ctrl+Shift+K. Custom bg, transcription for premium.", icon: ICON(null, 'โจ๏ธ') }, { key: 'sentencez', name: 'SentenceZ', emoji: '๐', desc: "Word knockoff. Every text media opens here.", icon: ICON(null, '๐') }, { key: 'imagez', name: 'ImageZ', emoji: '๐ผ๏ธ', desc: "Photoshop knockoff. Every image media opens here.", icon: ICON(null, '๐ผ๏ธ') }, { key: 'videoz', name: 'VideoZ', emoji: '๐น', desc: "Sony Vegas knockoff. Every video media opens here.", icon: ICON(null, '๐น') }, { key: 'lilith', name: 'Lilith', emoji: '๐๐ฝ', desc: "Apple Things knockoff โ task & life capture. Inbox โ Today โ Upcoming โ Logbook.", icon: ICON(null, '๐๐ฝ') }, { key: 'singz', name: 'SingZ', emoji: '๐ฉ๐ผโ๐ค', desc: "Singing practice app. RoutineZ, ExerciseZ, Session, VoiceMap, Coach.", icon: ICON('assets/icons/singz.jpg', '๐ฉ๐ผโ๐ค') }, { key: 'rapz', name: 'RapZ', emoji: '๐จ๐ผโ๐ค', desc: "Rapping practice app. FlowZ, RhymeZ, RapMap, every style on file.", icon: ICON('assets/icons/rapz.jpg', '๐จ๐ผโ๐ค') }, { key: 'bodiez', name: 'BodieZ', emoji: '๐ช๐ฝ', desc: "Jefit-style fitness companion. Routines, Session, BodyMap, Coach.", icon: ICON(null, '๐ช๐ฝ') }, { key: 'dawz', name: 'DawZ', emoji: '๐๏ธ', desc: "Digital Audio WorkstationZ. The in-app studio.", icon: ICON('assets/icons/dawz.jpg', '๐๏ธ') } ] }, { key: 'postz', label: 'PostZ', emoji: '๐ฃ', title: '๐ฃ PostZ โ Your output', corey: "Every track, image, lyric, idea โ it's a PostZ. PostZ feed everything else: BattleZ, DistributeZ, CollabZ all read from your PostZ.", subapps: [ { key: 'create', name: 'Create PostZ', emoji: 'โก', desc: "Make a new post: audio, image, video, or text.", icon: ICON('assets/icons/energy.jpg', 'โก') }, { key: 'builder', name: 'PostZ Builder', emoji: '๐๏ธ', desc: "Manage and edit your post library.", icon: ICON('assets/icons/builder.jpg', '๐๏ธ') }, { key: 'feed', name: 'Feed', emoji: '๐ฐ', desc: "Public feed of PostZ from people you follow.", icon: ICON('assets/icons/postz.jpg', '๐ฐ') } ] }, { key: 'ratez', label: 'RateZ', emoji: 'โญ', title: 'โญ RateZ โ AI Media Ratings', corey: "AI scores every PostZ on audio quality, visual impact, creativity, engagement. Community thumbs in too. Live feed of what's hot this week.", subapps: [ { key: 'top', name: 'Top Rated', emoji: '๐', desc: "This week's top-scored PostZ.", icon: ICON('assets/icons/ratez.jpg', '๐') }, { key: 'live', name: 'Live Feed', emoji: '๐ก', desc: "New ratings as they happen.", icon: ICON(null, '๐ก') }, { key: 'community', name: 'Community', emoji: '๐ฌ', desc: "Written reviews from other users.", icon: ICON(null, '๐ฌ') } ] }, { key: 'venuez', label: 'VenueZ', emoji: '๐๏ธ', title: '๐๏ธ VenueZ โ Go places with your music', corey: "Venues, open mics, festivals, showcases mapped near you. Filter by city, capacity, genre fit.", subapps: [ { key: 'near', name: 'Near Me', emoji: '๐', desc: "Venues close to your location.", icon: ICON('assets/icons/venuez.jpg', '๐') }, { key: 'book', name: 'Book', emoji: '๐ซ', desc: "Submit a booking request.", icon: ICON(null, '๐ซ') }, { key: 'mybookings', name: 'My Bookings', emoji: '๐ ', desc: "Your confirmed and pending shows.", icon: ICON(null, '๐ ') } ] }, { key: 'intelligence', label: 'Intelligence', emoji: '๐ง ', title: '๐ง Intelligence โ AI Media Creation', corey: "AI side of the house: generate audio, art, video, voice, 3D. Inputs in, media files out. Free tier gets a taste, premium goes deep.", subapps: [ { key: 'audio', name: 'Audio AI', emoji: '๐', desc: "AI-generated sound, music, voice.", icon: ICON('assets/icons/intelligence.jpg', '๐') }, { key: 'image', name: 'Image AI', emoji: '๐ผ๏ธ', desc: "AI-generated images and covers.", icon: ICON(null, '๐ผ๏ธ') }, { key: 'video', name: 'Video AI', emoji: '๐๏ธ', desc: "AI-generated video clips.", icon: ICON(null, '๐๏ธ') }, { key: 'voice', name: 'Voice AI', emoji: '๐ค', desc: "Voice clones, narration, dubbing.", icon: ICON(null, '๐ค') } ] }, { key: 'originalz', label: 'OriginalZ', emoji: '๐งฌ', title: '๐งฌ OriginalZ โ Unique SongZ verification', corey: "Audio-DNA scan against the catalog. Confirms a song is original before BattleZ accepts it or DistributeZ pushes it out.", subapps: [] }, { key: 'settingz', label: 'SettingZ', emoji: 'โ๏ธ', title: 'โ๏ธ SettingZ', corey: "Theme, notifications, location, account. Standard.", subapps: [] } ]; /* ======================================================================== ROUTING โ hash-based, single-page, so every tab and sub-app modal gets a real URL like #/toolz/lilith. Matches the spec exactly while staying deployable to any static host. ======================================================================== */ function currentRoute() { const h = location.hash.replace(/^#\//, '').replace(/^#/, ''); return h || 'home'; } function route(path) { // path can be 'home' or 'toolz/lilith' location.hash = '#/' + path; } window.addEventListener('hashchange', renderRoute); function renderRoute() { if (!state.auth.loggedIn) { // Force back to login if someone deep-links into an app URL while logged out document.getElementById('screen-login').style.display = 'flex'; document.getElementById('screen-app').style.display = 'none'; return; } document.getElementById('screen-login').style.display = 'none'; document.getElementById('screen-app').style.display = 'block'; const parts = currentRoute().split('/'); const tabKey = parts[0]; const subKey = parts[1]; renderTabBar(tabKey); renderPage(tabKey); if (subKey) openSubapp(tabKey, subKey); else closeModal(true); // close any open sub-app modal when navigating to bare tab } function renderTabBar(activeKey) { const bar = document.getElementById('tabBar'); bar.innerHTML = TABS.map(t => `${t.emoji}${t.label}` ).join(''); // Scroll the active tab into view so it isn't off-screen on mobile const active = bar.querySelector('.tab-link.active'); if (active) active.scrollIntoView({ inline: 'center', behavior: 'smooth', block: 'nearest' }); } /* ======================================================================== PAGE RENDERERS Each top-level tab has a renderer. Pages that don't need bespoke layout fall through to renderGenericTab, which lays out the sub-apps as cards. ======================================================================== */ function renderPage(tabKey) { const tab = TABS.find(t => t.key === tabKey) || TABS[0]; const content = document.getElementById('appContent'); document.getElementById('fab').classList.remove('show'); // Header is consistent across pages let html = `
${tab.title}
${tab.corey}
`; // Dispatch to bespoke renderers, fall through to generic if (tabKey === 'home') html += renderHomePage(); else if (tabKey === 'royaltiez') html += renderRoyaltiezPage(); else if (tabKey === 'groupz') html += renderGroupzPage(); else if (tabKey === 'labelz') html += renderLabelzPage(); else if (tabKey === 'settingz') html += renderSettingzPage(); else if (tabKey === 'bugz') html += renderBugzPage(); else if (tabKey === 'messagez') html += renderMessagezPage(); else html += renderGenericTab(tab); content.innerHTML = html; } function renderGenericTab(tab) { if (!tab.subapps.length) { return `
Coming soon. This tab has its own URL and will populate as features land.
Pick a persona, then pick the skills you actually do. Skills are dated โ that's how the app shows experience without you typing years.
`; _modalState = { selectedSkills: new Set(), personaKey: null }; showModal('Add Persona', html); } function renderPersonaSkillPicker() { const sel = val('addPersonaSel'); _modalState.personaKey = sel; _modalState.selectedSkills = new Set(); const wrap = document.getElementById('addSkillPicker'); if (!sel) { wrap.innerHTML = ''; return; } const p = PERSONA_DB[sel]; wrap.innerHTML = Object.entries(p.categories).map(([cat, skills]) => `
${cat}
${skills.map(s => { const any = s.toLowerCase().includes('any'); return ``; }).join('')}
`).join(''); } function toggleSkill(btn, name) { btn.classList.toggle('sel'); if (btn.classList.contains('sel')) _modalState.selectedSkills.add(name); else _modalState.selectedSkills.delete(name); } function saveNewPersona() { if (!_modalState.personaKey) return toast('Pick a persona first'); if (_modalState.selectedSkills.size === 0) return toast('Pick at least one skill'); const date = val('addSkillDate'); const def = PERSONA_DB[_modalState.personaKey]; let persona = state.personas.find(p => p.key === _modalState.personaKey); if (!persona) { persona = { key: _modalState.personaKey, name: def.label, skills: [] }; state.personas.push(persona); } for (const skill of _modalState.selectedSkills) { persona.skills.push({ name: skill, startDate: date }); } saveState(); closeModal(); renderPage('home'); toast(`โ Added ${_modalState.selectedSkills.size} skill(s) to ${def.label}`); } function removePersona(i) { if (!confirm('Remove this persona and all its skills?')) return; state.personas.splice(i, 1); saveState(); renderPage('home'); } /* ---- Royaltiez (wallet) ---- */ function renderRoyaltiezPage() { return `
`; } function onFabClick() { const r = currentRoute().split('/')[0]; if (r === 'bugz') openBugSubmitModal(); } function openBugSubmitModal() { showModal('Submit a Bug', `
`); } function submitBug() { const title = val('bug-title'), body = val('bug-body'); if (!title || !body) return toast('Title and details required'); state.bugs.push({ title, body, status: 'new', date: new Date().toLocaleString() }); saveState(); closeModal(); renderPage('bugz'); toast('โ Bug submitted'); } /* ---- MessageZ ---- */ function renderMessagezPage() { return `
๐ฅ Inbox (${state.messages.inbox.length})
${state.messages.inbox.length === 0 ? `
Empty.
` : state.messages.inbox.map(m => `
${esc(m.from)}
${esc(m.body)}
`).join('')}
๐ค Outbox (${state.messages.outbox.length})
${state.messages.outbox.length === 0 ? `
Empty.
` : state.messages.outbox.map(m => `
To: ${esc(m.to)}
${esc(m.body)}
`).join('')}
`; } /* ======================================================================== COREY VOICE INFO MODAL ======================================================================== */ function openCoreyModal(tabKey) { const tab = TABS.find(t => t.key === tabKey); if (!tab) return; const subapps = tab.subapps.length ? `
Apps in this tab
${tab.subapps.map(s => `
${s.emoji} ${s.name} โ ${s.desc}
`).join('')}
` : ''; showModal(`${tab.emoji} ${tab.label}`, `
${tab.corey}
URL: musicconnectz.net/${tab.key}
${subapps}`); } /* ======================================================================== SUB-APP MODALS (one per tab/subkey combo). Lilith, SingZ, RapZ, BodieZ each get nested tabs inside the modal. ======================================================================== */ function openSubapp(tabKey, subKey) { const tab = TABS.find(t => t.key === tabKey); const sub = tab?.subapps.find(s => s.key === subKey); if (!sub) { closeModal(true); return; } // Specialized contents for the practice apps if (tabKey === 'toolz' && subKey === 'lilith') return showModal('๐๐ฝ Lilith โ Tasks', renderLilithBody()); if (tabKey === 'toolz' && subKey === 'singz') return showModal('๐ฉ๐ผโ๐ค SingZ โ Singing Practice', renderSingzBody()); if (tabKey === 'toolz' && subKey === 'rapz') return showModal('๐จ๐ผโ๐ค RapZ โ Rapping Practice', renderRapzBody()); if (tabKey === 'toolz' && subKey === 'bodiez') return showModal('๐ช๐ฝ BodieZ โ Body Training', renderBodiezBody()); if (tabKey === 'toolz' && subKey === 'dawz') return showModal('๐๏ธ DawZ โ Digital Audio Workstation', renderDawzBody()); // Generic sub-app modal โ explains what the app does and opens the work area showModal(`${sub.emoji} ${sub.name}`, `
Workspace UI for ${sub.name} mounts here. Hook it into your existing component when ready.
`); } /* ---- Lilith (Apple Things knockoff) ---- */ const LILITH_TABS = [ ['inbox', '๐ฅ Inbox', "Where new tasks land before you organize them into projects, areas, or scheduled lists."], ['today', 'โผ๏ธ Today', "Tasks you plan to work on now."], ['upcoming','โ Upcoming', "Scheduled tasks and calendar-linked planning views."], ['anytime', '๐๐ผ Anytime', "Tasks you can do whenever without committing them to a specific date."], ['someday', 'โ Someday', "Ideas you may want to do later but don't want active in your current workflow."], ['logbook', '๐งพ Logbook', "Completed items move here."], ['trash', '๐ฎ Trash', "Deleted items before permanent deletion."] ]; function renderLilithBody() { return nestedTabs('lilith', LILITH_TABS); } /* ---- SingZ ---- */ const SINGZ_TABS = [ ['inbox','๐ฅ Inbox','New vocal ideas, song choices, lyrics, melody concepts, practice notes, teacher feedback land here.'], ['today','๐๏ธ Today','Today\'s singing practice: warmups, breathwork, drills, assigned songs, range work, cooldowns, voice check-in.'], ['upcoming','๐ Upcoming','Scheduled practices, lessons, recording sessions, performance prep, audition deadlines, vocal rest days.'], ['anytime','๐ถ Anytime','Quick vocal drills: lip trills, breath drills, pitch matching, scale runs, ear training.'], ['someday','๐ง Someday','Songs, techniques, styles, riffs, runs, performance ideas you may want to explore later.'], ['routinez','๐งฉ RoutineZ','Saved practice plans: Daily Warmup, R&B Runs, Rap-Sing Control, Breath Control, Pitch Accuracy, Range Expansion.'], ['exercisez','๐ ExerciseZ','Searchable library by skill type, difficulty, range, style, purpose, duration.'], ['session','โฑ๏ธ Session','Live practice mode: timers, takes, pitch accuracy, fatigue ratings, audio clips for review.'], ['songz','๐ผ SongZ','Songs you are learning, writing, recording, covering, rehearsing, or performing.'], ['recordingz','๐ง RecordingZ','Practice clips, vocal takes, before-and-after comparisons, song drafts.'], ['lessonz','๐ LessonZ','Guided learning paths, technique breakdowns, mini-courses, teacher assignments.'], ['progress','๐ Progress','Streaks, total practice time, range expansion, pitch accuracy, breath duration.'], ['voicemap','๐ฃ๏ธ VoiceMap','Visual development across pitch, range, breath, tone, agility, vibrato, resonance.'], ['coach','๐ค Coach','Smart recommendations on what to practice next based on weak spots, goals, fatigue.'], ['goalz','๐ฏ GoalZ','Vocal targets: expand range, improve pitch, master vibrato, prepare for audition.'], ['recovery','๐ Recovery','Vocal fatigue, hydration, sleep, rest, strain warnings, cooldown needs.'], ['community','๐ Community','Share progress, post covers, join challenges, get feedback.'], ['logbook','๐งพ Logbook','Completed practices, finished lessons, mastered songs, old recordings.'], ['trash','๐ฎ Trash','Deleted items before permanent deletion.'] ]; function renderSingzBody() { return nestedTabs('singz', SINGZ_TABS); } /* ---- RapZ ---- */ const RAPZ_TABS = [ ['inbox','๐ฅ Inbox','New rap ideas, punchlines, bars, rhyme schemes, song concepts, beat links, freestyle notes land here.'], ['today','๐ค Today','Today\'s rap practice: breath drills, flow exercises, freestyle prompts, cadence practice, delivery work.'], ['upcoming','๐ Upcoming','Scheduled practices, studio sessions, writing blocks, release deadlines, cypher prep.'], ['anytime','๐ฅ Anytime','Quick drills: freestyle warmups, 16-bar writing, rhyme stacking, breath control, pocket practice.'], ['someday','๐ง Someday','Future rap ideas, song concepts, styles, flows, alter egos, mixtape ideas.'], ['routinez','๐งฉ RoutineZ','Daily Freestyle, 16-Bar Builder, Punchline Training, Breath Control, Chopper Flow, Studio Prep.'], ['exercisez','๐ ExerciseZ','Browse drills by skill, difficulty, style, BPM, purpose, duration.'], ['session','โฑ๏ธ Session','Live practice: writing drills, freestyle over beats, timers, BPM comfort, delivery ratings.'], ['lyricz','๐ LyricZ','Bars, verses, hooks, bridges, punchlines, rhyme stacks, concepts, storytelling drafts.'], ['songz','๐ผ SongZ','Tracks you are writing, recording, rehearsing, mixing, performing, releasing.'], ['beatz','๐ฅ BeatZ','Beats, instrumentals, loops, producer links, BPM, key, mood, genre, licensing notes.'], ['stylez','๐ญ StyleZ','Switch flows, study genre-specific delivery, match writing to the right beat.'], ['alternative','๐ธ Alternative Rap','Experimental lyrics, genre-bending sounds, unusual flows, indie production.'], ['boombap','๐ฅ Boom Bap','Classic hip-hop writing, hard drums, sharp rhyme schemes, head-nod flow.'], ['chopper','๐ Chopper','Fast rap, rapid syllable control, breath timing, double-time flow.'], ['cloud','โ๏ธ Cloud Rap','Atmospheric flows, dreamy delivery, melodic pockets, vibe writing.'], ['conscious','๐ง Conscious Rap','Message-driven lyrics, social commentary, storytelling, deeper concepts.'], ['crunk','๐ฅ Crunk','High-energy chants, crowd control, aggressive hooks, hype practice.'], ['drill','โ๏ธ Drill','Dark beats, sliding flows, aggressive delivery, street cadence.'], ['emo','๐ค Emo Rap','Emotional lyrics, melodic rap, vulnerability, raw delivery.'], ['gfunk','๐ด G-Funk','West Coast flows, smooth pockets, funky basslines, relaxed cadence.'], ['gangsta','โ๏ธ Gangsta Rap','Gritty storytelling, street narratives, confident delivery, cinematic verses.'], ['hardcore','๐ค Hardcore Hip Hop','Aggressive bars, battle energy, complex rhyme schemes.'], ['jazz','๐ท Jazz Rap','Smooth flows, poetic lyrics, live-instrument beats, abstract writing.'], ['mumble','๐ค Mumble Rap','Vibe-first delivery, melodic repetition, hypnotic flows, ad-libs.'], ['oldschool','๐ป Old School','Foundational flow patterns, simple rhyme schemes, party cadence.'], ['snap','๐ซฐ Snap','Minimal beats, catchy hooks, danceable rhythms, chant delivery.'], ['trap','๐๏ธ Trap','Triplet flows, hi-hat pockets, ad-libs, 808 cadence, modern rap.'], ['recordingz','๐ง RecordingZ','Freestyle clips, practice takes, verse drafts, ad-lib tests, before/after.'], ['freestylez','๐ FreestyleZ','Off-the-top rapping, prompts, cyphers, beat shuffles, no-pen drills.'], ['flowz','๐ FlowZ','Cadence, pockets, rhythm changes, triplets, double-time, beat riding.'], ['rhymez','๐ค RhymeZ','Rhyme banks, multisyllabic, internal, slant, compound, wordplay patterns.'], ['delivery','๐ Delivery','Vocal presence, tone, aggression, emotion, clarity, projection, character.'], ['goalz','๐ฏ GoalZ','Targets: write 100 bars, freestyle 5min, master Chopper, finish a mixtape.'], ['coach','๐ค Coach','Suggestions based on weak spots, BPM comfort, rhyme complexity, deadlines.'], ['progresz','๐ ProgresZ','Writing streaks, completed verses, freestyle duration, BPM range, recording quality.'], ['rapmap','๐บ๏ธ RapMap','Visual development across flow, lyrics, breath, delivery, punchlines, hooks.'], ['community','๐ Community','Share freestyles, cyphers, challenges, producers, artist network.'], ['logbook','๐งพ Logbook','Completed practices, finished verses, mastered styles, old freestyles.'], ['trash','๐ฎ Trash','Deleted items before permanent deletion.'] ]; function renderRapzBody() { return nestedTabs('rapz', RAPZ_TABS); } /* ---- BodieZ ---- */ const BODIEZ_TABS = [ ['inbox','๐ฅ Inbox','Workout ideas, exercises, goals, notes, routine concepts land here.'], ['today','๐ช Today','Today\'s workout: routine, warmup, exercises, sets, reps, rest, cardio, recovery, check-in.'], ['upcoming','๐ Upcoming','Scheduled workouts, rest days, progress check-ins, weigh-ins, deloads.'], ['anytime','๐๏ธ Anytime','Pump sessions, mobility flows, ab circuits, cardio finishers, hotel workouts.'], ['someday','๐ง Someday','Future programs, exercises, challenges, diet phases, transformation goals.'], ['routines','๐งฉ Routines','PPL, Upper/Lower, Full Body, Bro Split, Strength, Hypertrophy, Fat Loss, Home.'], ['exercises','๐ Exercises','Browse by muscle, equipment, difficulty, movement pattern, goal, location.'], ['session','โฑ๏ธ Session','Live mode: log sets, reps, weight, duration, distance, RPE, RIR, rest, notes.'], ['progress','๐ Progress','PRs, total volume, consistency, bodyweight, 1RM estimates, muscle group frequency.'], ['bodymap','๐ง BodyMap','Heat map of trained, undertrained, overtrained muscles.'], ['coach','๐ค Coach','Increase weight, hold, deload, swap exercises, add/reduce volume.'], ['goals','๐ฏ Goals','Body, strength, habit, and performance targets.'], ['recovery','๐ Recovery','Soreness, sleep, fatigue, rest days, deloads, hydration, mobility, injuries.'], ['nutrition','๐ฅ Nutrition','Calories, protein, water, meal notes, supplements, body comp goals.'], ['community','๐ Community','Follow friends, join challenges, share routines, progress photos.'], ['logbook','๐งพ Logbook','Completed workouts, body check-ins, progress photos, PRs, notes.'], ['trash','๐ฎ Trash','Deleted items before permanent deletion.'] ]; function renderBodiezBody() { return nestedTabs('bodiez', BODIEZ_TABS); } function renderDawzBody() { return `
DawZ is the in-app studio โ tracks, EQ, compression, automation, mixer, master bus, vector scope. The full DAW UI mounts here. Use this modal as the placeholder shell.
DAW UI mounts here.
`; } /* Helper that draws a nested-tab block for app-in-app views */ function nestedTabs(appKey, tabs) { return `