function lingoOyunu
% LINGOOYUNU - Lingo tarzi 5 harfli kelime tahmin oyunu (Responsive + Koyu Tema Modal)

    %% ---------- Sabitler ----------
    ROWS = 6;
    COLS = 5;
    GAME_TIME_LIMIT = 60; % Varsayılan süre (saniye)
    thisFolder = fileparts(mfilename('fullpath'));

    KEY_LAYOUT.tr = { ...
        {'e','r','t','y','u','i_dotless','o','p','g_breve','u_umlaut'}, ...
        {'a','s','d','f','g','h','j','k','l','s_cedilla','i'}, ...
        {'ENTER','z','c','v','b','n','m','o_umlaut','c_cedilla','BACK'} };
    KEY_LAYOUT.en = { ...
        {'q','w','e','r','t','y','u','i','o','p'}, ...
        {'a','s','d','f','g','h','j','k','l'}, ...
        {'ENTER','z','x','c','v','b','n','m','BACK'} };

    SPECIAL.i_dotless = char(305);
    SPECIAL.g_breve   = char(287);
    SPECIAL.u_umlaut  = char(252);
    SPECIAL.s_cedilla = char(351);
    SPECIAL.o_umlaut  = char(246);
    SPECIAL.c_cedilla = char(231);

    TXT.tr = struct( ...
        'setupTitle', 'Kelime Avi''na Hos Geldin', ...
        'setupDesc', sprintf(['5 harfli gizli kelimeyi 6 denemede bul.\n' ...
            'Belirledigin sure icinde olabildigince cok kelime bilmeye calis!']), ...
        'timeLabel', 'Sure (Dakika):', ...
        'langLabel', 'Dil:', ...
        'start', 'BASLA', ...
        'refresh', char([8635 32 89 101 110 105 108 101]), ...
        'tooShort', 'Yetersiz harf', ...
        'win', 'Tebrikler! +1 Puan (+10s)', ...
        'lose', 'Bilemedin! Kelime: %s', ...
        'attempts', 'Deneme %d / 6', ...
        'score', 'Skor: %d', ...
        'time', 'Sure: %ds', ...
        'gameOverTitle', 'SURE BITTI!', ...
        'scoreLabel', 'Toplam Skorunuz', ...
        'wordLabel', 'Bilemediginiz Son Kelime', ...
        'restartBtn', 'TEKRAR BASLA', ...
        'exitBtn', 'ANA MENU (CIK)', ...
        'mainMenu', 'CIKIS', ...
        'enter', 'GIR');
    TXT.en = struct( ...
        'setupTitle', 'Welcome to Word Hunt', ...
        'setupDesc', sprintf(['Guess the 5-letter word in 6 tries.\n' ...
            'Try to guess as many words as possible!']), ...
        'timeLabel', 'Time (Minutes):', ...
        'langLabel', 'Language:', ...
        'start', 'START', ...
        'refresh', char([8635 32 78 101 119]), ...
        'tooShort', 'Not enough letters', ...
        'win', 'Nice! +1 Point (+10s)', ...
        'lose', 'Out of tries! Word: %s', ...
        'attempts', 'Attempt %d / 6', ...
        'score', 'Score: %d', ...
        'time', 'Time: %ds', ...
        'gameOverTitle', 'TIME UP!', ...
        'scoreLabel', 'Total Score', ...
        'wordLabel', 'Last Unguessed Word', ...
        'restartBtn', 'PLAY AGAIN', ...
        'exitBtn', 'MAIN MENU', ...
        'mainMenu', 'EXIT', ...
        'enter', 'ENTER');

    COLOR.bg      = [0.071 0.094 0.122];
    COLOR.panel   = [0.102 0.133 0.173];
    COLOR.ink     = [0.933 0.949 0.965];
    COLOR.accent  = [0.910 0.639 0.239];
    COLOR.green   = [0.298 0.604 0.416];
    COLOR.yellow  = [0.820 0.647 0.239];
    COLOR.gray    = [0.220 0.278 0.337];
    COLOR.danger  = [0.761 0.357 0.290];

    %% ---------- Durum Degiskenleri ----------
    lang        = 'tr';
    wordPoolTR  = {};
    wordPoolEN  = {};
    wordPool    = {};
    usedWords   = {}; 
    secretWord  = '';
    curRow      = 1;
    curCol      = 1;
    board       = repmat({''}, ROWS, COLS);
    lockedGreen = repmat({''}, 1, COLS);
    finished    = false;
    autoTimer   = [];
    gameTimer   = [];
    timeLeft    = GAME_TIME_LIMIT;
    score       = 0;

    cellLabels   = gobjects(ROWS, COLS);
    keyboardGrid = [];
    keyButtons   = containers.Map();

    %% ---------- Kelime Listelerini Yukle ----------
    wordPoolTR = loadWordList('tr');
    wordPoolEN = loadWordList('en');
    wordPool = wordPoolTR;

    %% ---------- Arayuz Olustur ----------
    fig = uifigure('Name', 'Kelime Avi - Lingo Tarzi', ...
        'Position', [100 60 500 800], 'Color', COLOR.bg, 'Resize', 'on');
    fig.WindowKeyPressFcn = @onPhysicalKey;
    fig.CloseRequestFcn = @onCloseFigure;

    % ANA KATMAN GRID (Modal Pop-up'larin ustte durabilmesi icin)
    outerGrid = uigridlayout(fig, [1 1]);
    outerGrid.Padding = [0 0 0 0];
    outerGrid.BackgroundColor = COLOR.bg;

    % OYUN VE SETUP GRID
    mainGrid = uigridlayout(outerGrid, [2 1]);
    mainGrid.Layout.Row = 1;
    mainGrid.Layout.Column = 1;
    mainGrid.RowHeight = {50, '1x'};
    mainGrid.BackgroundColor = COLOR.bg;
    mainGrid.Padding = [10 10 10 10];

    % ---- Baslik ----
    header = uigridlayout(mainGrid, [1 3]);
    header.Layout.Row = 1;
    header.Layout.Column = 1;
    header.ColumnWidth = {'1x', 80, 40};
    header.BackgroundColor = COLOR.bg;
    header.Padding = [0 0 0 0];

    titleLbl = uilabel(header, 'Text', 'KELIME AVI', ...
        'FontSize', 22, 'FontWeight', 'bold', 'FontColor', COLOR.accent);
    
    exitGameBtn = uibutton(header, 'Text', TXT.tr.mainMenu, ...
        'FontWeight', 'bold', 'FontSize', 12, ...
        'BackgroundColor', COLOR.danger, 'FontColor', [1 1 1], ...
        'Visible', 'off', 'ButtonPushedFcn', @(~,~) returnToSetup());

    helpBtn = uibutton(header, 'Text', '?', 'FontWeight', 'bold', ...
        'ButtonPushedFcn', @(~,~) showHelp());

    % ---- Setup Ekrani ----
    setupPanel = uigridlayout(mainGrid, [6 1]);
    setupPanel.Layout.Row = 2;
    setupPanel.Layout.Column = 1;
    setupPanel.RowHeight = {'1x', 80, 40, 40, 60, '1x'};
    setupPanel.BackgroundColor = COLOR.bg;
    % setupPanel.BackgroundColor = COLOR.green;

    setupTitleLbl = uilabel(setupPanel, 'Text', TXT.tr.setupTitle, ...
        'FontSize', 22, 'FontWeight', 'bold', 'FontColor', COLOR.accent, ...
        'HorizontalAlignment', 'center', 'VerticalAlignment', 'bottom');

    setupDescLbl = uilabel(setupPanel, 'Text', TXT.tr.setupDesc, ...
        'FontSize', 14, 'FontColor', [0.58 0.64 0.70], ...
        'HorizontalAlignment', 'center', 'VerticalAlignment', 'top');

    % Sure Ayari Paneli (Dakika Cinsinden)
    timeSetupRow = uigridlayout(setupPanel, [1 2]);
    timeSetupRow.ColumnWidth = {'1x', 80};
    timeSetupRow.BackgroundColor = COLOR.bg;
    timeSetupRow.Padding = [50 0 50 0];
    
    timeSetupLbl = uilabel(timeSetupRow, 'Text', TXT.tr.timeLabel, ...
        'FontSize', 14, 'FontWeight', 'bold', 'FontColor', COLOR.accent, ...
        'HorizontalAlignment', 'right', 'VerticalAlignment', 'center');
    
    timeSpinner = uispinner(timeSetupRow, 'Limits', [1 60], 'Value', 1, ...
        'FontSize', 14, 'FontWeight', 'bold', ...
        'BackgroundColor', COLOR.bg, 'FontColor', COLOR.accent);

    % Dil Secim Paneli (Etiketli)
    langSetupRow = uigridlayout(setupPanel, [1 2]);
    langSetupRow.ColumnWidth = {'1x', 80};
    langSetupRow.BackgroundColor = COLOR.bg;
    langSetupRow.Padding = [50 0 50 0];

    langSetupLbl = uilabel(langSetupRow, 'Text', TXT.tr.langLabel, ...
        'FontSize', 14, 'FontWeight', 'bold', 'FontColor', COLOR.accent, ...
        'HorizontalAlignment', 'right', 'VerticalAlignment', 'center');

    langSwitch = uiswitch(langSetupRow, 'slider', 'Items', {'TR','EN'}, ...
        'Value', 'TR','FontColor', COLOR.accent, 'ValueChangedFcn', @onLangSwitch);

    startBtn = uibutton(setupPanel, 'Text', TXT.tr.start, ...
        'FontWeight', 'bold', 'FontSize', 18, ...
        'BackgroundColor', COLOR.accent, 'FontColor', [0.10 0.07 0.02], ...
        'ButtonPushedFcn', @onStartGame);

    uilabel(setupPanel);

    % ---- Oyun Ekrani ----
    gamePanel = uigridlayout(mainGrid, [4 1]);
    gamePanel.Layout.Row = 2;
    gamePanel.Layout.Column = 1;
    gamePanel.RowHeight = {40, 30, '1.5x', '1x'};
    gamePanel.BackgroundColor = COLOR.bg;
    gamePanel.Visible = 'off';

    % Ust Bilgi Paneli
    statusRow = uigridlayout(gamePanel, [1 4]);
    statusRow.ColumnWidth = {90, 80, '1x', 90};
    statusRow.BackgroundColor = COLOR.bg;
    statusRow.Padding = [0 0 0 0];

    timerLbl = uilabel(statusRow, 'Text', 'Sure: 60s', 'FontWeight', 'bold', ...
        'FontSize', 14, 'FontColor', COLOR.accent);
    scoreLbl = uilabel(statusRow, 'Text', 'Skor: 0', 'FontWeight', 'bold', ...
        'FontSize', 14, 'FontColor', COLOR.green);
    attemptsLbl = uilabel(statusRow, 'Text', '', 'FontColor', [0.58 0.64 0.70], ...
        'FontSize', 14, 'HorizontalAlignment', 'center', 'VerticalAlignment', 'center');
    refreshBtn = uibutton(statusRow, 'Text', TXT.tr.refresh, ...
        'ButtonPushedFcn', @(~,~) startNewRound(false));

    % Mesaj Alani
    messageLbl = uilabel(gamePanel, 'Text', '', 'FontWeight', 'bold', ...
        'FontSize', 16, 'FontColor', COLOR.accent, 'HorizontalAlignment', 'center');

    % Kelime Izgarasi
    boardGrid = uigridlayout(gamePanel, [ROWS COLS]);
    boardGrid.BackgroundColor = COLOR.bg;
    boardGrid.RowSpacing = 8;
    boardGrid.ColumnSpacing = 8;
    boardGrid.Padding = [20 10 20 10];
    boardGrid.RowHeight = repmat({'1x'}, 1, ROWS);
    boardGrid.ColumnWidth = repmat({'1x'}, 1, COLS);
    
    for r = 1:ROWS
        for c = 1:COLS
            cellLabels(r,c) = uilabel(boardGrid, 'Text', '', ...
                'FontSize', 28, 'FontWeight', 'bold', ...
                'HorizontalAlignment', 'center', ...
                'VerticalAlignment', 'center', ...
                'FontColor', COLOR.ink, ...
                'BackgroundColor', COLOR.panel);
        end
    end

    % Klavye Izgarasi
    keyboardGrid = uigridlayout(gamePanel, [3 1]);
    keyboardGrid.BackgroundColor = COLOR.bg;
    keyboardGrid.Padding = [0 10 0 0];
    keyboardGrid.RowSpacing = 5;
    buildKeyboard();

    % ---- Koyu Tema Oyun Sonu Modal (Pop-Up) Katmani ----
    gameOverOverlay = uigridlayout(outerGrid, [1 1]);
    gameOverOverlay.Layout.Row = 1;
    gameOverOverlay.Layout.Column = 1;
    gameOverOverlay.BackgroundColor = [0.03 0.04 0.05];
    gameOverOverlay.Padding = [30 140 30 140];
    gameOverOverlay.Visible = 'off';

    modalBox = uigridlayout(gameOverOverlay, [6 1]);
    modalBox.BackgroundColor = COLOR.panel;
    modalBox.Padding = [20 20 20 20];
    modalBox.RowSpacing = 10;
    modalBox.RowHeight = {40, 45, 45, '1x', 45, 45};

    modalTitle = uilabel(modalBox, 'Text', TXT.tr.gameOverTitle, ...
        'FontSize', 22, 'FontWeight', 'bold', 'FontColor', COLOR.accent, ...
        'HorizontalAlignment', 'center');

    modalScoreLbl = uilabel(modalBox, 'Text', '', ...
        'FontSize', 16, 'FontWeight', 'bold', 'FontColor', COLOR.ink, ...
        'HorizontalAlignment', 'center');

    modalWordLbl = uilabel(modalBox, 'Text', '', ...
        'FontSize', 15, 'FontWeight', 'bold', 'FontColor', COLOR.danger, ...
        'HorizontalAlignment', 'center');

    uilabel(modalBox);

    modalRestartBtn = uibutton(modalBox, 'Text', TXT.tr.restartBtn, ...
        'FontSize', 15, 'FontWeight', 'bold', ...
        'BackgroundColor', COLOR.green, 'FontColor', [1 1 1], ...
        'ButtonPushedFcn', @(~,~) restartFromModal());

    modalExitBtn = uibutton(modalBox, 'Text', TXT.tr.exitBtn, ...
        'FontSize', 15, 'FontWeight', 'bold', ...
        'BackgroundColor', COLOR.gray, 'FontColor', COLOR.ink, ...
        'ButtonPushedFcn', @(~,~) exitFromModal());

    % ---- Koyu Tema Yardim Modal (Pop-Up) Katmani ----
    helpOverlay = uigridlayout(outerGrid, [1 1]);
    helpOverlay.Layout.Row = 1;
    helpOverlay.Layout.Column = 1;
    helpOverlay.BackgroundColor = [0.03 0.04 0.05];
    helpOverlay.Padding = [30 160 30 160];
    helpOverlay.Visible = 'off';

    helpBox = uigridlayout(helpOverlay, [3 1]);
    helpBox.BackgroundColor = COLOR.panel;
    helpBox.Padding = [20 20 20 20];
    helpBox.RowSpacing = 15;
    helpBox.RowHeight = {40, '1x', 45};

    helpTitle = uilabel(helpBox, 'Text', 'Nasıl Oynanır?', ...
        'FontSize', 22, 'FontWeight', 'bold', 'FontColor', COLOR.accent, ...
        'HorizontalAlignment', 'center');

    helpDescLbl = uilabel(helpBox, 'Text', TXT.tr.setupDesc, ...
        'FontSize', 14, 'FontColor', COLOR.ink, ...
        'HorizontalAlignment', 'center', 'VerticalAlignment', 'center', ...
        'WordWrap', 'on');

    helpCloseBtn = uibutton(helpBox, 'Text', 'TAMAM', ...
        'FontSize', 15, 'FontWeight', 'bold', ...
        'BackgroundColor', COLOR.accent, 'FontColor', [0.10 0.07 0.02], ...
        'ButtonPushedFcn', @(~,~) closeHelpModal());

    %% ================= FONKSIYONLAR =================

    function onLangSwitch(src, ~)
        if strcmp(src.Value, 'TR')
            lang = 'tr';
        else
            lang = 'en';
        end
        wordPool = pickPool();
        setupTitleLbl.Text = TXT.(lang).setupTitle;
        setupDescLbl.Text  = TXT.(lang).setupDesc;
        helpDescLbl.Text   = TXT.(lang).setupDesc;
        timeSetupLbl.Text  = TXT.(lang).timeLabel;
        langSetupLbl.Text  = TXT.(lang).langLabel;
        exitGameBtn.Text   = TXT.(lang).mainMenu;
        startBtn.Text      = TXT.(lang).start;
    end

    function p = pickPool()
        if strcmp(lang, 'tr'), p = wordPoolTR; else, p = wordPoolEN; end
    end

    function onStartGame(~,~)
        setupPanel.Visible = 'off';
        gamePanel.Visible = 'on';
        exitGameBtn.Visible = 'on';
        refreshBtn.Text = TXT.(lang).refresh;
        buildKeyboard();
        
        usedWords = {};
        score = 0;
        GAME_TIME_LIMIT = timeSpinner.Value * 60;
        timeLeft = GAME_TIME_LIMIT;
        
        scoreLbl.Text = sprintf(TXT.(lang).score, score);
        timerLbl.Text = sprintf(TXT.(lang).time, timeLeft);
        
        startNewRound(false);
        startMainTimer();

        focus(fig);
    end

    function startMainTimer()
        clearTimers();
        gameTimer = timer('ExecutionMode', 'fixedRate', ...
            'Period', 1.0, ...
            'TasksToExecute', inf, ...
            'TimerFcn', @onTimerTick);
        start(gameTimer);
    end

    function onTimerTick(~, ~)
        if ~isvalid(fig), return; end
        timeLeft = timeLeft - 1;
        if timeLeft <= 0
            timeLeft = 0;
            timerLbl.Text = sprintf(TXT.(lang).time, timeLeft);
            clearTimers();
            finished = true;
            showGameOverWindow();
        else
            timerLbl.Text = sprintf(TXT.(lang).time, timeLeft);
        end
    end

    function showGameOverWindow()
        modalTitle.Text    = TXT.(lang).gameOverTitle;
        modalScoreLbl.Text = sprintf('%s: %d', TXT.(lang).scoreLabel, score);
        modalWordLbl.Text  = sprintf('%s: %s', TXT.(lang).wordLabel, trUpperChar(secretWord));
        modalRestartBtn.Text = TXT.(lang).restartBtn;
        modalExitBtn.Text    = TXT.(lang).exitBtn;
        gameOverOverlay.Visible = 'on';
    end

    function restartFromModal()
        gameOverOverlay.Visible = 'off';
        onStartGame();
    end

    function exitFromModal()
        gameOverOverlay.Visible = 'off';
        returnToSetup();
    end

    function returnToSetup()
        clearTimers();
        finished = true;
        gamePanel.Visible = 'off';
        exitGameBtn.Visible = 'off';
        setupPanel.Visible = 'on';
    end

    function startNewRound(autoPassed)
        if ~isempty(autoTimer) && isvalid(autoTimer)
            stop(autoTimer); delete(autoTimer);
        end
        if timeLeft <= 0, return; end

        finished = false;
        wordPool = pickPool();
        
        availPool = setdiff(wordPool, usedWords, 'stable');
        if isempty(availPool)
            usedWords = {};
            availPool = wordPool;
        end
        
        secretWord = availPool{randi(numel(availPool))};
        usedWords{end+1} = secretWord;
        
        curRow = 1; curCol = 1;
        board = repmat({''}, ROWS, COLS);
        lockedGreen = repmat({''}, 1, COLS);
        
        lockedGreen{1} = secretWord(1);
        curCol = 2;
        
        if ~autoPassed
            messageLbl.Text = '';
        end
        attemptsLbl.Text = sprintf(TXT.(lang).attempts, 1);
        resetBoardColors();
        resetKeyboardColors();
        updateKeyColor(secretWord(1), 'green');
        renderBoard();
        
        focus(fig);
    end

    function resetBoardColors()
        for r = 1:ROWS
            for c = 1:COLS
                cellLabels(r,c).BackgroundColor = COLOR.panel;
                cellLabels(r,c).Text = '';
            end
        end
    end

    function resetKeyboardColors()
        ks = keys(keyButtons);
        for i = 1:numel(ks)
            b = keyButtons(ks{i});
            if isvalid(b)
                b.BackgroundColor = [0.125 0.169 0.216];
                b.FontColor = COLOR.ink;
            end
        end
    end

    function buildKeyboard()
        if isempty(keyboardGrid) || ~isvalid(keyboardGrid), return; end
        delete(keyboardGrid.Children);
        keyButtons = containers.Map();
        layout = KEY_LAYOUT.(lang);
        keyboardGrid.RowHeight = repmat({'1x'}, 1, numel(layout));
        for r = 1:numel(layout)
            rowKeys = layout{r};
            rowGrid = uigridlayout(keyboardGrid, [1 numel(rowKeys)]);
            rowGrid.BackgroundColor = COLOR.bg;
            rowGrid.Padding = [0 0 0 0];
            rowGrid.ColumnSpacing = 4;
            for c = 1:numel(rowKeys)
                keyId = rowKeys{c};
                displayTxt = keyLabel(keyId);
                b = uibutton(rowGrid, 'Text', displayTxt, ...
                    'FontWeight', 'bold', 'FontSize', 14, ...
                    'BackgroundColor', [0.125 0.169 0.216], ...
                    'FontColor', COLOR.ink, ...
                    'ButtonPushedFcn', @(~,~) handleKey(keyId));
                keyButtons(keyId) = b;
            end
        end
    end

    function txt = keyLabel(keyId)
        if strcmp(keyId, 'ENTER')
            txt = TXT.(lang).enter;
        elseif strcmp(keyId, 'BACK')
            txt = char(9003);
        else
            txt = trUpperChar(actualLetter(keyId));
        end
    end

    function ch = actualLetter(keyId)
        if isfield(SPECIAL, keyId)
            ch = SPECIAL.(keyId);
        else
            ch = keyId;
        end
    end

    function handleKey(keyId)
        if finished || timeLeft <= 0, return; end
        if strcmp(keyId, 'ENTER')
            submitGuess();
            return;
        end
        if strcmp(keyId, 'BACK')
            doBackspace();
            return;
        end
        typeLetter(actualLetter(keyId));
    end

    function typeLetter(ch)
        while curCol <= COLS && ~isempty(lockedGreen{curCol})
            curCol = curCol + 1;
        end
        if curCol > COLS, return; end
        board{curRow, curCol} = ch;
        curCol = curCol + 1;
        while curCol <= COLS && ~isempty(lockedGreen{curCol})
            curCol = curCol + 1;
        end
        renderBoard();
    end

    function doBackspace()
        c = curCol - 1;
        while c >= 1 && ~isempty(lockedGreen{c})
            c = c - 1;
        end
        if c < 1, return; end
        board{curRow, c} = '';
        curCol = c;
        renderBoard();
    end

    function renderBoard()
        for c = 1:COLS
            letter = board{curRow, c};
            isLocked = false;
            if isempty(letter) && ~isempty(lockedGreen{c})
                letter = lockedGreen{c};
                isLocked = true;
            end
            if isempty(letter)
                cellLabels(curRow, c).Text = '';
                cellLabels(curRow, c).BackgroundColor = COLOR.panel;
                cellLabels(curRow, c).FontColor = COLOR.ink;
            else
                cellLabels(curRow, c).Text = trUpperChar(letter);
                if isLocked
                    cellLabels(curRow, c).BackgroundColor = COLOR.green;
                    cellLabels(curRow, c).FontColor = [1 1 1];
                else
                    cellLabels(curRow, c).BackgroundColor = COLOR.panel;
                    cellLabels(curRow, c).FontColor = COLOR.ink;
                end
            end
        end
    end

    function tf = rowComplete()
        tf = true;
        for c = 1:COLS
            letter = board{curRow, c};
            if isempty(letter), letter = lockedGreen{c}; end
            if isempty(letter), tf = false; return; end
        end
    end

    function s = guessString()
        s = '';
        for c = 1:COLS
            letter = board{curRow, c};
            if isempty(letter), letter = lockedGreen{c}; end
            s = [s letter]; %#ok<AGROW>
        end
    end

    function submitGuess()
        if finished || timeLeft <= 0, return; end
        if ~rowComplete()
            flashMessage(TXT.(lang).tooShort, COLOR.danger);
            return;
        end
        guess = lower(guessString());
        messageLbl.Text = '';

        secretChars = char(secretWord);
        guessChars  = char(guess);
        result = repmat({'gray'}, 1, COLS);
        usedSecret = false(1, COLS);

        for c = 1:COLS
            if guessChars(c) == secretChars(c)
                result{c} = 'green';
                usedSecret(c) = true;
            end
        end
        for c = 1:COLS
            if strcmp(result{c}, 'green'), continue; end
            found = false;
            for s = 1:COLS
                if ~usedSecret(s) && secretChars(s) == guessChars(c)
                    found = true; usedSecret(s) = true; break;
                end
            end
            if found, result{c} = 'yellow'; end
        end

        for c = 1:COLS
            switch result{c}
                case 'green'
                    cellLabels(curRow, c).BackgroundColor = COLOR.green;
                    cellLabels(curRow, c).FontColor = [1 1 1];
                case 'yellow'
                    cellLabels(curRow, c).BackgroundColor = COLOR.yellow;
                    cellLabels(curRow, c).FontColor = [0.13 0.10 0.02];
                otherwise
                    cellLabels(curRow, c).BackgroundColor = COLOR.gray;
                    cellLabels(curRow, c).FontColor = [1 1 1];
            end
            updateKeyColor(guessChars(c), result{c});
            if strcmp(result{c}, 'green')
                lockedGreen{c} = guessChars(c);
            end
        end

        solved = strcmp(guess, secretWord);

        if solved
            finished = true;
            score = score + 1;
            timeLeft = timeLeft + 10;
            scoreLbl.Text = sprintf(TXT.(lang).score, score);
            timerLbl.Text = sprintf(TXT.(lang).time, timeLeft);
            flashMessage(TXT.(lang).win, COLOR.green);
            scheduleAutoAdvance();
            return;
        end

        curRow = curRow + 1;
        curCol = 2;
        if curRow > ROWS
            finished = true;
            flashMessage(sprintf(TXT.(lang).lose, trUpperChar(secretWord)), COLOR.danger);
            scheduleAutoAdvance();
            return;
        end
        attemptsLbl.Text = sprintf(TXT.(lang).attempts, curRow);
        renderBoard();
    end

    function updateKeyColor(ch, state)
        if ~isKey(keyButtons, char(ch))
            fn = fieldnames(SPECIAL);
            for i = 1:numel(fn)
                if SPECIAL.(fn{i}) == ch
                    applyKeyColor(fn{i}, state);
                    return;
                end
            end
            return;
        end
        applyKeyColor(char(ch), state);
    end

    function applyKeyColor(keyId, state)
        if ~isKey(keyButtons, keyId), return; end
        b = keyButtons(keyId);
        if ~isvalid(b), return; end
        if isequal(b.BackgroundColor, COLOR.green), return; end
        switch state
            case 'green'
                b.BackgroundColor = COLOR.green; b.FontColor = [1 1 1];
            case 'yellow'
                if ~isequal(b.BackgroundColor, COLOR.green)
                    b.BackgroundColor = COLOR.yellow; b.FontColor = [0.13 0.10 0.02];
                end
            case 'gray'
                if ~isequal(b.BackgroundColor, COLOR.green) && ~isequal(b.BackgroundColor, COLOR.yellow)
                    b.BackgroundColor = [0.14 0.18 0.22]; b.FontColor = [0.35 0.42 0.49];
                end
        end
    end

    function flashMessage(msg, color)
        messageLbl.Text = msg;
        messageLbl.FontColor = color;
    end

    function scheduleAutoAdvance()
        if ~isempty(autoTimer) && isvalid(autoTimer)
            stop(autoTimer); delete(autoTimer);
        end
        autoTimer = timer('ExecutionMode', 'singleShot', 'StartDelay', 1.2, ...
            'TimerFcn', @(~,~) startNewRound(true));
        start(autoTimer);
    end

    function showHelp()
        helpDescLbl.Text = TXT.(lang).setupDesc;
        helpOverlay.Visible = 'on';
    end

    function closeHelpModal()
        helpOverlay.Visible = 'off';
        focus(fig);
    end

    function onPhysicalKey(~, event)
        if strcmp(gamePanel.Visible, 'off') || timeLeft <= 0, return; end
        k = event.Key;
        if strcmp(k, 'return')
            handleKey('ENTER');
        elseif strcmp(k, 'backspace')
            handleKey('BACK');
        elseif numel(event.Character) == 1 && isletter(event.Character)
            typeLetter(lower(event.Character));
        end
    end

    function clearTimers()
        tList = timerfindall;
        if ~isempty(tList)
            stop(tList);
            delete(tList);
        end
    end

    function onCloseFigure(~, ~)
        clearTimers();
        delete(fig);
    end

    %% ---------- Kelime Listesi Yukleme ----------
    function words = loadWordList(langCode)
        localFile = fullfile(thisFolder, [langCode '_kelimeler.txt']);
        words = {};
        if isfile(localFile)
            try
                lines = readlines(localFile);
                lines = lines(strlength(lines) > 0);
                words = cellstr(lower(lines));
            catch
                words = {};
            end
        end
        if isempty(words)
            try
                if strcmp(langCode, 'en')
                    url = ['https://gist.githubusercontent.com/wchargin/8927565/' ...
                           'raw/d9783627c731268fb2935a731a618aa8e95cf465/words'];
                else
                    url = ['https://raw.githubusercontent.com/CanNuhlar/' ...
                           'Turkce-Kelime-Listesi/master/turkce_kelime_listesi.txt'];
                end
                raw = readlines(url);
                words = filterFiveLetter(raw, langCode);
            catch
                error('lingoOyunu:noWordList', ...
                    'Kelime listesi yuklenemedi. "%s_kelimeler.txt" dosyasini kontrol edin.', langCode);
            end
        end
    end

    function words = filterFiveLetter(lines, langCode)
        lines = lines(strlength(lines) > 0);
        keep = false(numel(lines), 1);
        for i = 1:numel(lines)
            w = char(lines(i));
            if strlength(lines(i)) ~= 5, continue; end
            if isempty(w) || ~isstrprop(w(1), 'lower'), continue; end
            if any(isspace(w)) || any(w == '-') || any(w == '''')
                continue;
            end
            keep(i) = true;
        end
        words = cellstr(lower(lines(keep)));
        words = unique(words);
    end

    function s = trUpperChar(str)
        str = char(str);
        s = str;
        for i = 1:numel(str)
            if str(i) == 'i'
                s(i) = char(304);
            elseif str(i) == char(305)
                s(i) = 'I';
            else
                s(i) = upper(str(i));
            end
        end
    end
end