```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>NoirCode Studio</title>
    <link rel="stylesheet" href="./style/style.css">
</head>
<body>
    <header>
        <div>NoirCode Studio</div>
        <div class="tabs">
            <button id="tab-editor" class="active">Editor</button>
            <button id="tab-preview">Preview</button>
        </div>
    </header>
    <div class="container">
        <div class="editor active">
            <textarea id="code" placeholder="Type your NoirCode here...">
# This is a single-line comment

#{
This is a multi-line comment.
It spans multiple lines.
#}

h1{Welcome to NoirCode Studio}

p{NoirCode Studio is a powerful and intuitive editor designed for NoirCode, a streamlined markup language that enhances HTML development.}

h2{Features}

ul\{
    li{Real-time preview}
    li{Error detection}
    li{Sleek design}
\}

h2{Getting Started}

p{Begin by typing your NoirCode in the editor. Use the preview pane to see the rendered HTML instantly. NoirCode Studio supports advanced functionalities like nesting elements, adding attributes, and repeating elements efficiently.}

div[title="Index: &index;"]{&index;. Hi}*5

button[role="button" title="Click me"].click-me.click-me2{Click me}
button[role="button" title="Click me"].click-me.click-me1{Click me}

html\{
<button class="test">Test 2</button>
\}

script\{
document.querySelectorAll('.click-me').forEach(btn => {
    btn.addEventListener('click', () => {
        alert('Button clicked!');
    });
});
\}

style\{
.click-me {
    background-color: #3498db;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    margin: 5px;
}
.click-me:hover {
    background-color: #2980b9;
}
\}
            </textarea>
            <div id="error-message" class="error"></div>
        </div>
        <div class="preview">
            <iframe id="previewFrame" srcdoc="" frameborder="0" style="width: 100%; height: 100%; background-color: transparent;"></iframe>
        </div>
    </div>
    <script src="./script.js"></script>
</body>
</html>
```

```scss
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html,
body {
    height: 100%;
}

body {
    background-color: #1e1e1e;
    color: #cccccc;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    display: flex;
    flex-direction: column;
}

header {
    background-color: #252526;
    padding: 15px 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 1.5rem;
    font-weight: bold;
    border-bottom: 1px solid #333;
    position: relative;
}

.tabs {
    display: none;

    button {
        background: none;
        border: none;
        color: #cccccc;
        padding: 10px 15px;
        cursor: pointer;
        font-size: 1rem;
        transition: background-color 0.3s;

        &.active,
        &:hover {
            background-color: #3a3a3a;
            border-radius: 4px;
        }
    }
}

.container {
    flex: 1;
    display: flex;
    overflow: hidden;

    .editor,
    .preview {
        flex: 1;
        padding: 20px;
        overflow: auto;

        &.editor {
            background-color: #1e1e1e;
            border-right: 1px solid #333;
            display: flex;
            flex-direction: column;

            textarea {
                width: 100%;
                height: 100%;
                background-color: #1e1e1e;
                color: #cccccc;
                border: none;
                resize: none;
                font-family: 'Courier New', Courier, monospace;
                font-size: 1rem;
                outline: none;
            }
        }

        &.preview {
            background-color: #2d2d2d;
            overflow: hidden;
        }
    }
}

.error {
    color: #e74c3c;
    margin-top: 10px;
    font-size: 0.9rem;
    display: none;
}

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: #1e1e1e;
}

::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 4px;
}

@media (max-width: 768px) {
    .container {
        flex-direction: column;

        .editor,
        .preview {
            flex: 1;
            display: none;
            height: 100%;

            &.active {
                display: flex;
                flex-direction: column;
            }
        }

        .editor {
            border-right: none;
            border-bottom: 1px solid #333;
        }
    }

    header .tabs {
        display: flex;
    }

    .error {
        display: block;
    }
}
```

```javascript
const textarea = document.getElementById('code');
const previewFrame = document.getElementById('previewFrame');
const errorMessage = document.getElementById('error-message');

/**
 * Compiles custom code to HTML.
 * @param {string} customCode - The custom code to compile.
 * @returns {Object} - An object containing the compiled HTML and any errors.
 */
function compileToHTML(customCode) {
    const lines = customCode.split('\n');
    let html = '';
    let errors = [];
    let i = 0;
    const totalLines = lines.length;
    let inBlockComment = false;

    while (i < totalLines) {
        let line = lines[i].trim();

        if (inBlockComment) {
            if (line.endsWith('#}')) {
                inBlockComment = false;
            }
            i++;
            continue;
        }

        if (line.startsWith('#{')) {
            inBlockComment = true;
            i++;
            continue;
        }

        if (line.startsWith('#') && !line.startsWith('#{')) {
            i++;
            continue;
        }

        if (line === '\\}') {
            errors.push(`Unexpected block closure '\\}' at line ${i + 1}.`);
            i++;
            continue;
        }

        const blockStartMatch = line.match(/^(\w+)(\[[^\]]+\])?(#[\w\-]+)?((?:\.[\w\-]+)*?)\\\{$/);
        if (blockStartMatch) {
            const tag = blockStartMatch[1].toLowerCase();
            const attrs = blockStartMatch[2] ? replaceIndexPlaceholders(blockStartMatch[2].slice(1, -1), 1) : '';
            const id = blockStartMatch[3] ? replaceIndexPlaceholders(blockStartMatch[3].slice(1), 1) : '';
            const classesRaw = blockStartMatch[4] ? blockStartMatch[4] : '';
            const classes = classesRaw.split('.').filter(cls => cls !== '');

            let blockContent = '';
            i++;
            let openBlocks = 1;

            while (i < totalLines && openBlocks > 0) {
                let currentLine = lines[i].trim();

                if (currentLine.endsWith('\\{')) {
                    openBlocks++;
                }

                if (currentLine === '\\}') {
                    openBlocks--;
                    if (openBlocks === 0) {
                        i++;
                        break;
                    }
                }

                if (openBlocks > 0) {
                    blockContent += lines[i] + '\n';
                }

                i++;
            }

            if (openBlocks > 0) {
                errors.push(`Unclosed block starting with <${tag}>.`);
                break;
            }

            let innerHTML = '';

            if (tag === 'script' || tag === 'style' || tag === 'html') {
                innerHTML = blockContent;
            } else {
                const compiledBlock = compileToHTML(blockContent);
                if (compiledBlock.errors.length > 0) {
                    errors = errors.concat(compiledBlock.errors);
                }
                innerHTML = compiledBlock.html;
            }

            let attrString = '';
            if (attrs) attrString += ` ${attrs}`;
            if (id) attrString += ` id="${id}"`;
            if (classes.length > 0) attrString += ` class="${classes.join(' ')}"`;

            html += `<${tag}${attrString}>\n${innerHTML}</${tag}>\n`;

            continue;
        }

        const inlineMatch = line.match(/^(\w+)(\[[^\]]+\])?(#[\w\-]+)?((?:\.[\w\-]+)*?)(?:\{([^}]*)\})?(?:\*(\d+))?$/);
        if (inlineMatch) {
            const tag = inlineMatch[1].toLowerCase();
            const attrs = inlineMatch[2] ? replaceIndexPlaceholders(inlineMatch[2].slice(1, -1), 1) : '';
            const id = inlineMatch[3] ? replaceIndexPlaceholders(inlineMatch[3].slice(1), 1) : '';
            const classesRaw = inlineMatch[4] ? inlineMatch[4] : '';
            const classes = classesRaw.split('.').filter(cls => cls !== '');
            const content = inlineMatch[5] || '';
            const repeat = inlineMatch[6] ? parseInt(inlineMatch[6]) : 1;

            let finalAttrString = '';
            if (attrs) finalAttrString += ` ${attrs}`;
            if (id) finalAttrString += ` id="${id}"`;
            if (classes.length > 0) finalAttrString += ` class="${classes.join(' ')}"`;

            for (let j = 1; j <= repeat; j++) {
                const currentContent = replaceIndexPlaceholders(content, j);
                const currentAttrString = replaceIndexPlaceholders(finalAttrString, j);
                html += `<${tag}${currentAttrString}>${currentContent}</${tag}>\n`;
            }

            i++;
            continue;
        }

        const siblingTokens = splitRespectingBlocks(line, '+');
        siblingTokens.forEach(token => {
            const nestingTokens = splitRespectingBlocks(token, '>');
            const nestedHTML = processNesting(nestingTokens, 0, errors, i + 1);
            html += nestedHTML;
        });

        const textLineMatch = line.match(/^[^+\>]+$/);
        if (!blockStartMatch && !inlineMatch && textLineMatch) {
            html += `${escapeHTML(line)}\n`;
        }

        i++;
    }

    return { html, errors };

    /**
     * Recursive function to process nested tokens.
     * @param {Array} nestingTokens - Array of tokens split by '>'.
     * @param {number} index - Current index in the nestingTokens array.
     * @param {Array} errors - Array to collect error messages.
     * @param {number} lineNumber - Current line number for error reporting.
     * @returns {string} - The generated HTML string after processing nesting.
     */
    function processNesting(nestingTokens, index, errors, lineNumber) {
        if (index >= nestingTokens.length) return '';

        const nestedToken = nestingTokens[index].trim();
        const isLast = index === nestingTokens.length - 1;

        if (nestedToken === '') return '';

        let generatedHTML = '';

        try {
            const { tag, attributes, id, classes, content, repeat } = parseElement(nestedToken, lineNumber);
            let attrString = '';
            if (attributes) attrString += ` ${attributes}`;
            if (id) attrString += ` id="${id}"`;
            if (classes.length > 0) attrString += ` class="${classes.join(' ')}"`;

            for (let j = 1; j <= repeat; j++) {
                let finalContent = replaceIndexPlaceholders(content, j);
                let finalAttrString = replaceIndexPlaceholders(attrString, j);
                let finalId = replaceIndexPlaceholders(id, j);
                let finalClasses = classes.map(cls => replaceIndexPlaceholders(cls, j));

                if (finalId) {
                    finalAttrString = finalAttrString.replace(` id="${id}"`, ` id="${finalId}"`);
                }

                if (finalClasses.length > 0) {
                    const classRegex = /class="([^"]+)"/;
                    const match = finalAttrString.match(classRegex);
                    if (match) {
                        const existingClasses = match[1].split(' ');
                        const updatedClasses = [...new Set([...existingClasses, ...finalClasses])];
                        finalAttrString = finalAttrString.replace(classRegex, `class="${updatedClasses.join(' ')}"`);
                    } else {
                        finalAttrString += ` class="${finalClasses.join(' ')}"`;
                    }
                }

                generatedHTML += `<${tag}${finalAttrString}>`;

                if (finalContent) {
                    generatedHTML += finalContent;
                }

                if (!isLast) {
                    generatedHTML += '\n';
                    generatedHTML += processNesting(nestingTokens, index + 1, errors, lineNumber);
                    generatedHTML += `</${tag}>\n`;
                } else {
                    generatedHTML += `</${tag}>\n`;
                }
            }
        } catch (err) {
            if (nestedToken.startsWith('\\}')) {
                return '';
            } else {
                generatedHTML += `${escapeHTML(nestedToken)}\n`;
            }
        }

        return generatedHTML;
    }
}

/**
 * Splits a string by a delimiter but ignores delimiters inside quotes or blocks.
 * @param {string} str - The string to split.
 * @param {string} delimiter - The delimiter to split by.
 * @returns {Array} - Array of split tokens.
 */
function splitRespectingBlocks(str, delimiter) {
    const tokens = [];
    let current = '';
    let inQuotes = false;
    let quoteChar = '';
    let escape = false;
    let blockDepth = 0;

    for (let i = 0; i < str.length; i++) {
        let char = str[i];

        if (escape) {
            current += char;
            escape = false;
            continue;
        }

        if (char === '\\') {
            escape = true;
            current += char;
            continue;
        }

        if ((char === '"' || char === "'") && !inQuotes) {
            inQuotes = true;
            quoteChar = char;
            current += char;
            continue;
        }

        if (char === quoteChar && inQuotes) {
            inQuotes = false;
            quoteChar = '';
            current += char;
            continue;
        }

        if (!inQuotes && char === '{') {
            blockDepth++;
            current += char;
            continue;
        }

        if (!inQuotes && char === '}') {
            blockDepth = Math.max(blockDepth - 1, 0);
            current += char;
            continue;
        }

        if (!inQuotes && blockDepth === 0 && char === delimiter) {
            tokens.push(current);
            current = '';
            continue;
        }

        current += char;
    }

    if (current !== '') {
        tokens.push(current);
    }

    return tokens;
}

/**
 * Replaces all instances of &index; with the provided index number.
 * @param {string} str - The string to process.
 * @param {number} index - The index to replace &index; with.
 * @returns {string} - The processed string.
 */
function replaceIndexPlaceholders(str, index) {
    return str.replace(/&index;/g, index);
}

/**
 * Parses an element definition from the custom syntax.
 * @param {string} token - A token from the custom code.
 * @param {number} lineNumber - The line number for error reporting.
 * @returns {Object} - Parsed element details.
 */
function parseElement(token, lineNumber) {
    const regex = /^(\w+)(\[[^\]]+\])?(#[\w\-]+)?((?:\.[\w\-]+)*?)(?:\{([^}]*)\})?(?:\*(\d+))?$/
    const match = token.match(regex);

    if (!match) {
        throw { message: `Invalid syntax near "${token}".`, line: lineNumber };
    }

    const [, tag, attrs, id, classNames, content, repeat] = match;
    let attributes = '';
    let classes = [];

    if (attrs) {
        const attrContent = attrs.slice(1, -1);
        const attrRegex = /(\w+)="([^"]+)"/g;
        let attrMatch;
        while ((attrMatch = attrRegex.exec(attrContent)) !== null) {
            const [, attrName, attrValue] = attrMatch;
            attributes += ` ${attrName}="${attrValue}"`;
        }

        const validAttrFormat = [...attrContent.matchAll(attrRegex)].length > 0;
        if (attributes.trim() === '' && attrContent.trim() !== '') {
            throw { message: `Attributes must be in the format key="value".`, line: lineNumber };
        }
    }

    if (classNames) {
        classes = classNames.split('.').filter(cls => cls !== '').map(cls => cls.trim());
    }

    const element = {
        tag: tag,
        attributes: attributes,
        id: id ? id.slice(1) : '',
        classes: classes,
        content: content ? content : '',
        repeat: repeat ? parseInt(repeat) : 1
    };

    return element;
}

/**
 * Escapes HTML special characters in a string.
 * @param {string} str - The string to escape.
 * @returns {string} - The escaped string.
 */
function escapeHTML(str) {
    const replacements = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#039;'
    };
    return str.replace(/[&<>"']/g, match => replacements[match]);
}

function updatePreview() {
    const textarea = document.getElementById('code');
    const previewFrame = document.getElementById('previewFrame');
    const errorMessage = document.getElementById('error-message');

    const customCode = textarea.value;
    let compilationResult;
    try {
        compilationResult = compileToHTML(customCode);
    } catch (error) {
        compilationResult = { html: '', errors: [`Unexpected error: ${error.message}`] };
    }

    const { html, errors } = compilationResult;

    if (errors && errors.length > 0) {
        errorMessage.textContent = errors.join(' | ');
        errorMessage.style.display = 'block';
        previewFrame.srcdoc = '';
    } else {
        errorMessage.textContent = '';
        errorMessage.style.display = 'none';
        const fullHTML = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Preview</title>
    <style>
        body {
            background-color: #1e1e1e;
            color: #cccccc;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            padding: 20px;
        }
        h1, h2, h3, h4, h5, h6 {
            color: #ffffff;
        }
        p {
            color: #dcdcdc;
            margin-bottom: 10px;
        }
        button {
            padding: 10px 20px;
            background-color: #3498db;
            color: #fff;
            border: none;
            cursor: pointer;
            border-radius: 4px;
            margin-top: 10px;
        }
        button:hover {
            background-color: #2980b9;
        }
        a {
            color: #9b59b6;
        }
        a:hover {
            color: #8e44ad;
        }
        /* Additional styles for various elements */
        ul {
            list-style-type: disc;
            padding-left: 20px;
            margin-bottom: 10px;
        }
        li {
            margin-bottom: 5px;
        }
        input, textarea {
            background-color: #333333;
            color: #ffffff;
            border: 1px solid #555555;
            padding: 8px;
            border-radius: 4px;
            margin-top: 5px;
        }
        input:focus, textarea:focus {
            border-color: #3498db;
            outline: none;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 10px;
        }
        th, td {
            border: 1px solid #555555;
            padding: 8px;
            text-align: left;
        }
        th {
            background-color: #444444;
        }
        tr:nth-child(even) {
            background-color: #2d2d2d;
        }
        /* Embedded Styles */
        ${extractEmbeddedStyles(html)}
    </style>
</head>
<body>
${html}
</body>
</html>`;
        previewFrame.srcdoc = fullHTML;
    }
}

/**
 * Extracts embedded <style> blocks from the generated HTML to include in the preview's head.
 * @param {string} html - The generated HTML content.
 * @returns {string} - Extracted CSS from <style> blocks.
 */
function extractEmbeddedStyles(html) {
    const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
    let styles = '';
    let match;
    while ((match = styleRegex.exec(html)) !== null) {
        styles += match[1] + '\n';
    }
    return styles;
}

updatePreview();

let timeout;
const textareaElement = document.getElementById('code');

textareaElement.addEventListener('input', () => {
    clearTimeout(timeout);
    timeout = setTimeout(updatePreview, 300);
});

const tabEditor = document.getElementById('tab-editor');
const tabPreview = document.getElementById('tab-preview');
const editor = document.querySelector('.editor');
const preview = document.querySelector('.preview');

/**
 * Activates the specified tab and deactivates the other.
 * @param {string} tab - The tab to activate ('editor' or 'preview').
 */
function activateTab(tab) {
    if (tab === 'editor') {
        tabEditor.classList.add('active');
        tabPreview.classList.remove('active');
        editor.classList.add('active');
        preview.classList.remove('active');
    } else if (tab === 'preview') {
        tabPreview.classList.add('active');
        tabEditor.classList.remove('active');
        preview.classList.add('active');
        editor.classList.remove('active');
    }
}

tabEditor.addEventListener('click', () => activateTab('editor'));
tabPreview.addEventListener('click', () => activateTab('preview'));

textareaElement.addEventListener('keydown', e => {
    const openingChars = {
        '"': '"',
        "'": "'",
        '(': ')',
        '{': '}',
        '[': ']',
        '<': '>'
    };

    const closingChars = Object.values(openingChars);

    if (e.key === 'Backspace') {
        const cursorPos = textareaElement.selectionStart;
        const textBeforeCursor = textareaElement.value.substring(0, cursorPos);
        const lastTwoChars = textBeforeCursor.slice(-2);
        return;
    }

    if (e.key === '"' || e.key === "'") {
        const closingChar = openingChars[e.key];
        const cursorPos = textareaElement.selectionStart;
        const beforeCursor = textareaElement.value.substring(0, cursorPos);
        const afterCursor = textareaElement.value.substring(cursorPos);

        if (afterCursor.startsWith(closingChar)) {
            e.preventDefault();
            textareaElement.setSelectionRange(cursorPos + 1, cursorPos + 1);
        } else {
            e.preventDefault();
            textareaElement.value = beforeCursor + e.key + closingChar + afterCursor;
            setTimeout(() => {
                textareaElement.setSelectionRange(cursorPos + 1, cursorPos + 1);
                textareaElement.dispatchEvent(new Event('input'));
            }, 0);
        }
        return;
    }

    if (e.key in openingChars) {
        e.preventDefault();
        const closingChar = openingChars[e.key];
        const cursorPos = textareaElement.selectionStart;
        const beforeCursor = textareaElement.value.substring(0, cursorPos);
        const afterCursor = textareaElement.value.substring(cursorPos);

        textareaElement.value = beforeCursor + e.key + closingChar + afterCursor;
        setTimeout(() => {
            textareaElement.setSelectionRange(cursorPos + e.key.length, cursorPos + e.key.length);
            textareaElement.dispatchEvent(new Event('input'));
        }, 0);
    }

    if (closingChars.includes(e.key)) {
        const cursorPos = textareaElement.selectionStart;
        const nextChar = textareaElement.value.substring(cursorPos, cursorPos + 1);
        if (e.key === nextChar) {
            e.preventDefault();
            textareaElement.setSelectionRange(cursorPos + 1, cursorPos + 1);
        }
    }
});
```

---

As you can see, on mobile resolution devices, we switch to tabs rather than a split screen. However, when switching to the "Preview" tab from the "Editor" tab, it doesn't switch, it just keeps the Editor on top, and the preview on the bottom, which is not what we need, we want all the other tabs to be hidden when we're in 1 tab, that's how tabs work. On desktop, just we have to keep using the split screen layout. For brevity, don't rewrite the whole code, just the modified parts