<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>Bono's Fret Blaster Launcher</title>

<style>

  body {

    font-family: Arial, sans-serif;

    margin: 20px;

    text-align: center;

    background-color: #f9f9f9;

  }

  #launchBtn {

    font-size: 1.5em;

    padding: 15px 30px;

    background-color: #4CAF50;

    color: white;

    border: none;

    border-radius: 8px;

    cursor: pointer;

    transition: background-color 0.3s ease;

  }

  #launchBtn:hover {

    background-color: #45a049;

  }

  #gameContainer {

    margin-top: 40px;

  }

</style>

</head>

<body>

<h1 id="mainTitle">Welcome to Bono's Fret Blaster</h1> <button id="launchBtn">Launch the Game</button>

<div id="gameContainer"></div>

<script>

// HTML content for the game

const gameHTML = `

  <style>

    #game-container {

      display: flex;

      flex-direction: column;

      align-items: center;

    }

    /* Styles for the main fretboard container */

    #fretboard {

      display: grid; /* Enables grid layout */

      /* Defines 9 columns for frets (80px each) - no string label column */

      grid-template-columns: repeat(9, 80px);

      /* Defines 1 row for fret numbers (30px) and 6 rows for strings (50px each) */

      grid-template-rows: 30px repeat(6, 50px);

      grid-gap: 2px; /* Space between grid cells */

      margin-top: 20px;

      user-select: none;

      border: 2px solid #333; /* Outer border of the fretboard */

      padding: 5px; /* Padding inside the fretboard border */

      background-color: #555; /* Background of the fretboard itself */

    }

    /* Base style for all cells in the grid (labels and frets) */

    .fret-cell {

      border: 1px solid #444; /* Border for each individual cell */

      box-sizing: border-box; /* Ensures padding and border don't increase cell size */

      font-weight: bold;

      text-align: center;

      user-select: none;

      display: flex;

      align-items: center;

      justify-content: center;

      width: 100%; /* Make cell fill its grid area */

      height: 100%; /* Make cell fill its grid area */

    }

    /* Style for fret number labels (top row) */

    .fret-label {

      background-color: #666; /* Darker background for labels */

      color: white;

      cursor: default;

      pointer-events: none; /* Not interactive */

      font-size: 1em;

    }

    /* String labels are now removed, but keeping this class for potential future use or if styling is applied elsewhere */

    .string-label {

      background-color: #f0f0f0; /* Light background for labels */

      cursor: default;

      pointer-events: none; /* Not interactive */

      font-size: 1.1em;

    }

    /* Style for the clickable fret cells */

    .fret {

      background-color: #eee; /* Light background for clickable frets */

      cursor: pointer;

      transition: background-color 0.3s;

      font-size: 1.2em;

    }

    /* Style for Open (Fret 0) notes - initially correct and unclickable for guessing */

    .initial-correct {

        background-color: #e0ffe0 !important; /* Light green */

        cursor: default !important;

        pointer-events: none !important;

    }

    /* Hover effect for clickable frets that are not yet answered or wrong */

    .fret:hover:not(.correct):not(.wrong):not(.initial-correct) {

      background-color: #ddd; /* Slightly darker on hover */

    }

    /* Style for correctly guessed frets */

    .correct {

      background-color: #a8e6a1 !important; /* Green for correct */

      cursor: default; /* No longer needs pointer cursor */

    }

    /* Style for incorrectly guessed frets */

    .wrong {

      background-color: #f99 !important; /* Red for wrong */

    }

    /* Style for the prompt message (e.g., "Find the note: C") */

    #prompt {

      font-size: 1.5em;

      margin-top: 10px;

      color: #333; /* Ensure prompt text is visible */

    }

    /* Style for the prompt box */

    .prompt-box {

        background-color: #e0f2f7; /* Light blue */

        border: 1px solid #a7d9ed; /* Slightly darker blue border */

        padding: 10px 20px;

        border-radius: 8px;

        margin-bottom: 20px; /* Space below the box */

        display: inline-block; /* To make the box fit its content */

    }

    /* Style for the score box */

    .score-box {

        background-color: #e6ffe6; /* Very light green */

        border: 1px solid #a8e6a1; /* Slightly darker green border */

        padding: 10px 20px;

        border-radius: 8px;

        margin-top: 15px; /* Space above the box */

        display: inline-block; /* To make the box fit its content */

        font-size: 1.2em;

        font-weight: bold;

        color: #333;

    }

    #message {

      margin-top: 10px;

      font-weight: bold;

      height: 1.5em; /* Reserve space for messages */

      color: #333; /* Ensure message text is visible */

    }

    /* Removed #score style as it's now handled by .score-box */

    #playAgainBtn {

      margin-top: 20px;

      padding: 10px 20px;

      font-size: 1em;

      display: none; /* Hidden initially */

      cursor: pointer;

    }

    #chromatic-scale {

      border: 2px solid #444;

      padding: 10px 15px;

      max-width: 150px;

      text-align: left;

      user-select: none;

      margin-top: 30px;

      margin-left: auto;

      margin-right: auto;

    }

    #chromatic-scale h2 {

      margin-top: 0;

      margin-bottom: 10px;

      font-size: 1.3em;

      text-align: center;

    }

    .note-box {

      display: inline-block;

      width: 28px;

      height: 30px;

      line-height: 30px;

      margin: 2px;

      border: 1px solid #444;

      background-color: #eee;

      font-weight: bold;

      text-align: center;

      font-size: 1em;

      border-radius: 4px;

      user-select: none;

    }

    .sharp {

      background-color: #ccc;

    }

  </style>

  <div id="game-container">

    <h1>Bono's Fret Blaster</h1>

    <p>Click the fret on the guitar fretboard that matches the note shown.</p>

    <div id="prompt" class="prompt-box"></div>

    <div id="fretboard"></div>

    <div id="message"></div>

    <div id="score" class="score-box"></div>

    <button id="playAgainBtn">Play Again</button>

  </div>

  <div id="chromatic-scale">

    <h2>Chromatic Scale</h2>

  </div>

`;

// JavaScript content for the game logic

const gameScriptContent = `

  (function() {

    'use strict';

    // --- Constants ---

    const STANDARD_TUNING = ['E', 'A', 'D', 'G', 'B', 'E'];

    const CHROMATIC_NOTES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];

    const NUM_STRINGS = STANDARD_TUNING.length;

    const NUM_FRETS_TO_DISPLAY = 8; // For frets 0 (Open) to 7

    const MAX_TRIES = 20;

    // DOM Element IDs

    const DOM_ID_FRETBOARD = 'fretboard';

    const DOM_ID_PLAY_AGAIN_BTN = 'playAgainBtn';

    const DOM_ID_PROMPT = 'prompt';

    const DOM_ID_MESSAGE = 'message';

    const DOM_ID_SCORE = 'score';

    const DOM_ID_CHROMATIC_SCALE = 'chromatic-scale';

    // CSS Classes

    const CSS_CLASS_FRET_CELL = 'fret-cell';

    const CSS_CLASS_FRET_LABEL = 'fret-label';

    const CSS_CLASS_FRET = 'fret';

    const CSS_CLASS_INITIAL_CORRECT = 'initial-correct';

    const CSS_CLASS_CORRECT = 'correct';

    const CSS_CLASS_WRONG = 'wrong';

    const CSS_CLASS_PROMPT_BOX = 'prompt-box';

    const CSS_CLASS_SCORE_BOX = 'score-box';

    const SOUND_VOLUME_CORRECT_PRIMARY = 0.22;

    const SOUND_VOLUME_CORRECT_SECONDARY = 0.18;

    const SOUND_VOLUME_INCORRECT = 0.18;

    const CHROMATIC_NOTE_INDICES = {};

    CHROMATIC_NOTES.forEach((note, index) => CHROMATIC_NOTE_INDICES[note] = index);

    function getNoteFor(stringIndex, fretNumber) {

      const openNote = STANDARD_TUNING[stringIndex];

      const openNoteIndex = CHROMATIC_NOTE_INDICES[openNote];

      if (openNoteIndex === undefined) return '?';

      return CHROMATIC_NOTES[(openNoteIndex + fretNumber) % CHROMATIC_NOTES.length];

    }

    // --- DOM Elements ---

    const fretboardDiv = document.getElementById(DOM_ID_FRETBOARD);

    const playAgainBtn = document.getElementById(DOM_ID_PLAY_AGAIN_BTN);

    const promptDiv = document.getElementById(DOM_ID_PROMPT);

    const messageDiv = document.getElementById(DOM_ID_MESSAGE);

    const scoreDiv = document.getElementById(DOM_ID_SCORE);

    const chromaticScaleDiv = document.getElementById(DOM_ID_CHROMATIC_SCALE);

    if (!fretboardDiv || !promptDiv || !playAgainBtn || !messageDiv || !scoreDiv || !chromaticScaleDiv) {

        console.error("CRITICAL ERROR: One or more essential game HTML elements are missing. Check IDs like 'fretboard', 'prompt', etc.");

        alert("Error: Game elements missing. Cannot start Bono's Fret Blaster.");

        return;

    }

    // --- Game State ---

    let gameState = {

      currentNoteToFind: '',

      correctAnswers: 0,

      attemptsMade: 0,

      isGameActive: true

    };

    // --- Audio Context and Sound Functions ---

    const AudioContext = window.AudioContext || window.webkitAudioContext;

    let audioCtx;

    try {

      audioCtx = new AudioContext();

    } catch (e) {

      console.warn("Web Audio API is not supported or could not be initialized. Sound will be disabled.", e);

      var playSound = function() {};

      var playCorrectSound = function() {};

      var playIncorrectSound = function() {};

    }

    if (audioCtx) {

        function playSoundOriginal(frequency, duration, type = 'sine', volume = 0.1) {

          if (!audioCtx || audioCtx.state === 'suspended') {

              audioCtx && audioCtx.resume().catch(err => console.warn("Failed to resume audio context:", err));

          }

          if (!audioCtx || audioCtx.state !== 'running') {

              return;

          }

          const oscillator = audioCtx.createOscillator();

          const gainNode = audioCtx.createGain();

          oscillator.type = type;

          oscillator.frequency.value = frequency;

          gainNode.gain.setValueAtTime(volume, audioCtx.currentTime);

          gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);

          oscillator.connect(gainNode);

          gainNode.connect(audioCtx.destination);

          oscillator.start();

          oscillator.stop(audioCtx.currentTime + duration);

        }

        playCorrectSound = function() {

          playSoundOriginal(880, 0.15, 'triangle', SOUND_VOLUME_CORRECT_PRIMARY);

          setTimeout(() => playSoundOriginal(1320, 0.15, 'triangle', SOUND_VOLUME_CORRECT_SECONDARY), 50);

        };

        playIncorrectSound = function() {

          playSoundOriginal(150, 0.2, 'square', SOUND_VOLUME_INCORRECT);

        };

        playSound = playSoundOriginal;

    }

    // --- Fretboard Building ---

    function buildFretboard() {

      fretboardDiv.innerHTML = ''; // Clear previous board

      // Fret labels (0/Open through NUM_FRETS_TO_DISPLAY - 1)

      for (let fret = 0; fret < NUM_FRETS_TO_DISPLAY; fret++) {

        const fretLabel = document.createElement('div');

        fretLabel.textContent = fret === 0 ? 'Open' : fret;

        fretLabel.classList.add(CSS_CLASS_FRET_CELL, CSS_CLASS_FRET_LABEL);

        fretLabel.style.gridRow = 1;

        fretLabel.style.gridColumn = fret + 1;

        fretboardDiv.appendChild(fretLabel);

      }

      // String rows (high E string at the top visually)

      for (let stringIdx = NUM_STRINGS - 1; stringIdx >= 0; stringIdx--) {

        const visualGridRow = 2 + ((NUM_STRINGS - 1) - stringIdx);

        // Fret cells for this string

        for (let fret = 0; fret < NUM_FRETS_TO_DISPLAY; fret++) {

          const fretDiv = document.createElement('div');

          fretDiv.classList.add(CSS_CLASS_FRET_CELL, CSS_CLASS_FRET);

          fretDiv.dataset.string = stringIdx;

          fretDiv.dataset.fret = fret;

          // Set text content based on fret number

          if (fret === 0) { // Open string

            fretDiv.classList.add(CSS_CLASS_INITIAL_CORRECT);

            fretDiv.textContent = STANDARD_TUNING[stringIdx];

          } else {

            fretDiv.textContent = '';

          }

          fretDiv.style.gridRow = visualGridRow;

          fretDiv.style.gridColumn = fret + 1;

          fretboardDiv.appendChild(fretDiv);

        }

      }

    }

    // --- Game Logic ---

    function pickRandomNote() {

      const randomStringIdx = Math.floor(Math.random() * NUM_STRINGS);

      const randomFret = Math.floor(Math.random() * NUM_FRETS_TO_DISPLAY);

      return getNoteFor(randomStringIdx, randomFret);

    }

    function updateScore() {

      scoreDiv.textContent = \`Score: \${gameState.correctAnswers} / \${gameState.attemptsMade} (Best out of \${MAX_TRIES})\`;

    }

    function resetCellStatesForNewGame() {

        document.querySelectorAll(\`.\${CSS_CLASS_FRET}\`).forEach(el => {

            el.classList.remove(CSS_CLASS_CORRECT, CSS_CLASS_WRONG);

        });

    }

    function newRound() {

      if (gameState.attemptsMade >= MAX_TRIES) {

        promptDiv.textContent = \`Game Over! Final score: \${gameState.correctAnswers} / \${MAX_TRIES}.\`;

        messageDiv.textContent = 'Well done!';

        gameState.isGameActive = false;

        playAgainBtn.style.display = 'inline-block';

        updateScore();

        return;

      }

      document.querySelectorAll(\`.\${CSS_CLASS_FRET}.\${CSS_CLASS_WRONG}\`).forEach(el => {

          el.classList.remove(CSS_CLASS_WRONG);

          if (Number(el.dataset.fret) !== 0 && !el.classList.contains(CSS_CLASS_CORRECT)) {

              el.textContent = '';

          } else if (Number(el.dataset.fret) === 0) {

              el.textContent = STANDARD_TUNING[Number(el.dataset.string)];

          }

      });

      gameState.currentNoteToFind = pickRandomNote();

      promptDiv.textContent = \`Find the note: \${gameState.currentNoteToFind}\`;

      messageDiv.textContent = '';

      gameState.isGameActive = true;

      updateScore();

    }

    fretboardDiv.addEventListener('click', e => {

      if (!gameState.isGameActive || !e.target.classList.contains(CSS_CLASS_FRET) || e.target.classList.contains(CSS_CLASS_INITIAL_CORRECT)) {

        return;

      }

      const clickedFretCell = e.target;

      const stringNum = Number(clickedFretCell.dataset.string);

      const fretNum = Number(clickedFretCell.dataset.fret);

      const selectedNote = getNoteFor(stringNum, fretNum);

      if (clickedFretCell.classList.contains(CSS_CLASS_CORRECT)) {

        const previouslyCorrectNoteText = clickedFretCell.textContent;

        messageDiv.textContent = \`That's \${previouslyCorrectNoteText}, found earlier. Still looking for \${gameState.currentNoteToFind}.\`;

        return;

      }

      gameState.attemptsMade++;

      if (selectedNote === gameState.currentNoteToFind) {

        clickedFretCell.classList.remove(CSS_CLASS_WRONG);

        clickedFretCell.classList.add(CSS_CLASS_CORRECT);

        clickedFretCell.textContent = selectedNote;

        messageDiv.textContent = 'Correct! 🎉 Finding next note...';

        gameState.correctAnswers++;

        if (typeof playCorrectSound === 'function') playCorrectSound();

        newRound();

      } else {

        clickedFretCell.classList.add(CSS_CLASS_WRONG);

        clickedFretCell.textContent = selectedNote;

        messageDiv.textContent = \`Incorrect. That fret is \${selectedNote}. Target: \${gameState.currentNoteToFind}.\`;

        if (typeof playIncorrectSound === 'function') playIncorrectSound();

        updateScore();

        setTimeout(() => {

          if (clickedFretCell.classList.contains(CSS_CLASS_WRONG)) {

            clickedFretCell.classList.remove(CSS_CLASS_WRONG);

            if (fretNum !== 0 && !clickedFretCell.classList.contains(CSS_CLASS_CORRECT)) {

                clickedFretCell.textContent = '';

            } else if (fretNum === 0) {

                clickedFretCell.textContent = STANDARD_TUNING[stringNum];

            }

          }

        }, 1000);

        if (gameState.attemptsMade >= MAX_TRIES) {

          newRound();

        }

      }

    });

    playAgainBtn.addEventListener('click', () => {

      gameState.correctAnswers = 0;

      gameState.attemptsMade = 0;

      gameState.isGameActive = true;

      playAgainBtn.style.display = 'none';

      resetCellStatesForNewGame();

      buildFretboard();

      newRound();

    });

    function renderChromaticScale() {

      chromaticScaleDiv.innerHTML = '<h2>Chromatic Scale</h2>';

      CHROMATIC_NOTES.forEach(note => {

        const noteDiv = document.createElement('div');

        noteDiv.className = 'note-box' + (note.includes('#') ? ' sharp' : '');

        noteDiv.textContent = note;

        chromaticScaleDiv.appendChild(noteDiv);

      });

    }

    // --- Initialize Game ---

    buildFretboard();

    renderChromaticScale();

    newRound();

  })();

`;

const launchBtn = document.getElementById('launchBtn');

const gameContainer = document.getElementById('gameContainer');

const mainTitle = document.getElementById('mainTitle'); // Get the main title element

launchBtn.addEventListener('click', () => {

  if (gameContainer.innerHTML !== '') return;

  launchBtn.disabled = true;

  launchBtn.textContent = 'Loading Game...';

  // Hide the main title when the game launches

  if (mainTitle) {

      mainTitle.style.display = 'none';

  }

  if (window.AudioContext || window.webkitAudioContext) {

    let tempAudioCtx = new (window.AudioContext || window.webkitAudioContext)();

    if (tempAudioCtx.state === 'suspended') {

      tempAudioCtx.resume().then(() => {

        tempAudioCtx.close();

      }).catch(err => {

        tempAudioCtx.close();

      });

    } else {

       tempAudioCtx.close();

    }

  }

  gameContainer.innerHTML = gameHTML;

  const gameScriptElement = document.createElement('script');

  gameScriptElement.textContent = gameScriptContent;

  gameContainer.appendChild(gameScriptElement);

  launchBtn.style.display = 'none';

});

</script>

</body>

</html>