diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000000000..3e71214f8c8db
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,70 @@
+# Start from the official PHP 8.2 image with Apache
+FROM php:8.2-apache-bookworm
+
+# Install system dependencies, Node.js, Composer, and Cypress dependencies
+RUN apt-get update && apt-get install -y \
+ # System tools and git
+ git \
+ unzip \
+ curl \
+ sudo \
+ # PHP extensions dependencies
+ libpng-dev \
+ libonig-dev \
+ libxml2-dev \
+ libzip-dev \
+ # MySQL client AND server
+ default-mysql-client \
+ default-mysql-server \
+ # Cypress dependencies
+ xvfb \
+ libgtk2.0-0 \
+ libgtk-3-0 \
+ libgbm-dev \
+ libnotify-dev \
+ libnss3 \
+ libxss1 \
+ libasound2 \
+ libxtst6 \
+ xauth \
+ libldap2-dev \
+ libgd-dev \
+ && \
+ # Install the required PHP extensions for Zip and MySQL
+ docker-php-ext-install zip mysqli gd ldap && \
+ # Install Node.js (LTS version)
+ curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
+ apt-get install -y nodejs && \
+ # Install Composer globally
+ curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
+ # Install Xdebug
+ pecl install xdebug && \
+ # Clean up apt cache to reduce image size
+ apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# After installing everything
+RUN sed -i 's|/var/www/html|/workspaces/joomla-cms|g' /etc/apache2/sites-available/000-default.conf \
+ && sed -i 's|/var/www/html|/workspaces/joomla-cms|g' /etc/apache2/apache2.conf
+
+# Enable Apache's rewrite module and set the ServerName to prevent warnings
+RUN a2enmod rewrite \
+ && echo "ServerName localhost" >> /etc/apache2/apache2.conf
+
+RUN apt-get update && apt-get install -y ssl-cert && \
+ a2enmod ssl && \
+ a2ensite 000-default && \
+ a2ensite default-ssl && \
+ sed -i 's|/var/www/html|/workspaces/joomla-cms|g' /etc/apache2/sites-available/default-ssl.conf && \
+ echo '\n Options Indexes FollowSymLinks\n AllowOverride All\n Require all granted\n' >> /etc/apache2/sites-available/default-ssl.conf
+
+# Create a custom PHP configuration file to enable file uploads and a log directory
+RUN mkdir -p /var/log/ && \
+ touch /var/log/php_errors.log && \
+ chown -R www-data:www-data /var/log/ && \
+ chmod 766 /var/log/php_errors.log && \
+ echo "upload_tmp_dir = /tmp" > /usr/local/etc/php/conf.d/custom-php.ini && \
+ echo "post_max_size = 64M" >> /usr/local/etc/php/conf.d/custom-php.ini && \
+ echo "upload_max_filesize = 64M" >> /usr/local/etc/php/conf.d/custom-php.ini && \
+ echo "log_errors = On" >> /usr/local/etc/php/conf.d/custom-php.ini && \
+ echo "error_log = /var/log/php_errors.log" >> /usr/local/etc/php/conf.d/custom-php.ini && \
+ echo "error_reporting = E_ALL" >> /usr/local/etc/php/conf.d/custom-php.ini
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000000..9a93f372e6177
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,44 @@
+{
+ "name": "Joomla Dev Environment",
+ "dockerComposeFile": "docker-compose.yml",
+ "service": "app",
+ "workspaceFolder": "/workspaces/joomla-cms",
+ "features": {
+ "ghcr.io/devcontainers/features/desktop-lite:1": {}
+ },
+ "portsAttributes": {
+ "443" : {
+ "label": "Web Server (Joomla & phpmyadmin)",
+ "onAutoForward": "silent"
+ },
+ "6080": {
+ "label": "Cypress GUI",
+ "onAutoForward": "silent"
+ }
+ },
+ "postCreateCommand": "bash ./.devcontainer/post-create.sh",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "xdebug.php-debug",
+ "bmewburn.vscode-intelephense-client",
+ "esbenp.prettier-vscode"
+ ],
+ "settings": {
+ "launch": {
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Listen for Xdebug",
+ "type": "php",
+ "request": "launch",
+ "port": 9003,
+ "log": false
+ }
+ ]
+ }
+ }
+ }
+ },
+ "remoteUser": "root"
+}
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
new file mode 100644
index 0000000000000..0d47fdd66174a
--- /dev/null
+++ b/.devcontainer/docker-compose.yml
@@ -0,0 +1,29 @@
+services:
+ app:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ volumes:
+ - ..:/workspaces/joomla-cms:cached
+ - ./xdebug.ini:/usr/local/etc/php/conf.d/99-xdebug.ini
+ ports:
+ - "80:80"
+ - "443:443"
+ - "3306:3306"
+ - "6080:6080"
+ command: sleep infinity
+
+ mysql:
+ image: mysql:8.0
+ command: --default-authentication-plugin=mysql_native_password
+ restart: unless-stopped
+ environment:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_DATABASE: test_joomla
+ MYSQL_USER: joomla_ut
+ MYSQL_PASSWORD: joomla_ut
+ volumes:
+ - "mysql-data:/var/lib/mysql"
+
+volumes:
+ mysql-data:
diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh
new file mode 100644
index 0000000000000..804916075b835
--- /dev/null
+++ b/.devcontainer/post-create.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+
+# Exit immediately if a command exits with a non-zero status.
+set -e
+
+echo "--- Starting Joomla Core Post-Creation Setup ---"
+
+# Configuration variables
+DB_NAME="test_joomla"
+DB_USER="joomla_ut"
+DB_PASS="joomla_ut"
+ADMIN_USER="ci-admin"
+ADMIN_REAL_NAME="jane doe"
+ADMIN_PASS="joomla-17082005"
+ADMIN_EMAIL="admin@example.com"
+JOOMLA_ROOT="/workspaces/joomla-cms"
+
+# Allow git commands to run safely in the container
+git config --global --add safe.directory $JOOMLA_ROOT
+
+# --- 1. Wait for MariaDB Service ---
+echo "--> Waiting for MariaDB to become available..."
+while ! mysqladmin ping -h"mysql" --silent; do
+ sleep 1
+done
+echo "✅ MariaDB is ready."
+
+# --- 2. Install Core Dependencies ---
+echo "--> Installing Composer and NPM dependencies..."
+composer install
+npm install
+echo "✅ Dependencies installed."
+
+# --- 3. Install Joomla from Repository Source ---
+echo "--> Installing Joomla using the local repository source..."
+rm -f configuration.php
+php installation/joomla.php install \
+ --site-name="Joomla CMS Test" \
+ --admin-user="$ADMIN_REAL_NAME" \
+ --admin-username="$ADMIN_USER" \
+ --admin-password="$ADMIN_PASS" \
+ --admin-email="$ADMIN_EMAIL" \
+ --db-type="mysqli" \
+ --db-host="mysql" \
+ --db-name="$DB_NAME" \
+ --db-user="$DB_USER" \
+ --db-pass="$DB_PASS" \
+ --db-prefix="jos_" \
+ --db-encryption="0" \
+ --public-folder=""
+echo "✅ Joomla installed."
+
+# --- 4. Configure Joomla for Development ---
+echo "--> Applying development settings..."
+# Enable debug mode and maximum error reporting for easier troubleshooting.
+php cli/joomla.php config:set error_reporting=maximum
+echo "✅ Development settings applied."
+
+# --- 5. Install and Configure phpMyAdmin ---
+PMA_ROOT="${JOOMLA_ROOT}/phpmyadmin"
+echo "--> Downloading phpMyAdmin into $PMA_ROOT..."
+PMA_VERSION=5.2.2
+mkdir -p $PMA_ROOT
+curl -o /tmp/phpmyadmin.tar.gz https://files.phpmyadmin.net/phpMyAdmin/${PMA_VERSION}/phpMyAdmin-${PMA_VERSION}-all-languages.tar.gz
+tar xf /tmp/phpmyadmin.tar.gz --strip-components=1 -C $PMA_ROOT
+rm /tmp/phpmyadmin.tar.gz
+cp $PMA_ROOT/config.sample.inc.php $PMA_ROOT/config.inc.php
+sed -i "/\['AllowNoPassword'\] = false/a \$cfg['Servers'][\$i]['host'] = 'mysql';" $PMA_ROOT/config.inc.php
+
+# --- 6. Apply Codespaces Host Fix ---
+# This ensures Joomla generates correct URLs when accessed through the forwarded port.
+echo "--> Applying Codespaces URL fix..."
+cat > "${JOOMLA_ROOT}/fix.php" << 'EOF'
+ Ignoring local changes..."
+# For TRACKED files, tell Git to stop watching them for changes
+git update-index --assume-unchanged "index.php"
+git update-index --assume-unchanged "administrator/index.php"
+git update-index --assume-unchanged "package-lock.json"
+git update-index --assume-unchanged "tests/System/integration/install/Installation.cy.js"
+git update-index --assume-unchanged "tests/System/support/commands/config.mjs"
+
+# For NEW UNTRACKED files, add them to the local exclude file
+echo "cypress.config.js" >> ".git/info/exclude"
+echo "fix.php" >> ".git/info/exclude"
+echo "phpmyadmin" >> ".git/info/exclude"
+echo "codespace-details.txt" >> ".git/info/exclude"
+
+# --- 7. Finalize Permissions and Testing Tools ---
+echo "--> Setting up file permissions and Cypress..."
+sed -i \
+ -e "/\/\/ If exists, delete PHP configuration file to force a new installation/d" \
+ -e "/cy.task('deleteRelativePath', 'configuration.php');/d" \
+ -e "/cy.installJoomla(config);/d" \
+ tests/System/integration/install/Installation.cy.js
+sed -i "s/return cy.task('writeRelativeFile', { path: 'configuration.php', content });/return cy.task('writeRelativeFile', { path: 'configuration.php', content, mode: 0o775 });/" tests/System/support/commands/config.mjs
+
+# Ensure Cypress is executable and owned by the web server user
+chmod +x ./node_modules/.bin/cypress
+cp cypress.config.dist.mjs cypress.config.js
+npx cypress install
+sed -i -e "s|baseUrl:.*|baseUrl: 'https://localhost',|" -e "s/db_host: 'localhost'/db_host: 'mysql'/g" -e "s/db_user: 'root'/db_user: 'joomla_ut'/g" -e "s/db_password: ''/db_password: 'joomla_ut'/g" cypress.config.js
+
+# Restart Apache to apply all changes
+echo '
+ AllowOverride All
+ Require all granted
+' | sudo tee -a /etc/apache2/apache2.conf
+service apache2 restart
+
+# Set the group to www-data and enforce group permissions
+echo "--> Applying final group ownership and permissions..."
+chgrp -R www-data $JOOMLA_ROOT
+chmod -R g+rws $JOOMLA_ROOT
+
+echo "✅ Environment finalized."
+
+# --- 8. Display Setup Details ---
+# Save the details to a file for easy reference.
+DETAILS_FILE="${JOOMLA_ROOT}/codespace-details.txt"
+{
+ echo ""
+ echo "---"
+ echo "🚀 Joomla Core development environment is ready! 🚀"
+ echo ""
+ echo "This information has been saved to codespace-details.txt"
+ echo ""
+ echo "Joomla Admin Login:"
+ echo " URL: Open the 'Ports' tab, find the 'Web Server' (443), and click the Globe icon. Then add /administrator"
+ echo " Username: $ADMIN_USER"
+ echo " Password: $ADMIN_PASS"
+ echo ""
+ echo "phpMyAdmin Login:"
+ echo " URL: Open the 'Web Server' port and add /phpmyadmin"
+ echo " Username: $DB_USER"
+ echo " Password: $DB_PASS"
+ echo ""
+ echo "Cypress E2E Testing:"
+ echo " Run interactive tests: npx cypress open"
+ echo " Run headless tests: npx cypress run"
+ echo ""
+ echo "Xdebug for PHP Debugging:"
+ echo " Xdebug is pre-configured on port 9003. Use the 'Run and Debug' panel in VS Code and select 'Listen for Xdebug'."
+ echo "---"
+} | tee "$DETAILS_FILE"
diff --git a/.devcontainer/xdebug.ini b/.devcontainer/xdebug.ini
new file mode 100644
index 0000000000000..61b89fca18d38
--- /dev/null
+++ b/.devcontainer/xdebug.ini
@@ -0,0 +1,7 @@
+[xdebug]
+zend_extension=xdebug
+xdebug.mode=debug
+xdebug.log_level = 0
+xdebug.start_with_request=yes
+xdebug.client_port=9003
+xdebug.client_host=localhost