# A Magia da Cor — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build a single PHP landing page for PPG Dyrup's "A Magia da Cor" event registration, supporting two regions (Norte/Sul) via URL parameter, saving to MySQL, and sending confirmation emails via PHPMailer.

**Architecture:** Single `index.php` handles both rendering and POST processing. Region data (events, stores) is defined in `config.php`. Email sending is encapsulated in `send_email.php`. No framework — vanilla PHP with PDO.

**Tech Stack:** PHP 7.4+, PDO + MySQL, PHPMailer (already present in repo), HTML5/CSS3

---

## File Map

| File | Responsibility |
|------|----------------|
| `index.php` | Render HTML, validate `?regiao`, process POST, show success/error |
| `config.php` | DB credentials, SMTP credentials, `$eventos`, `$lojas` arrays |
| `send_email.php` | `enviar_confirmacao(string $nome, string $email, string $evento): bool` |
| `assets/style.css` | All visual styles — hero, form, typography |
| `assets/logo-dyrup.png` | Dyrup logo (to be added manually) |
| `assets/bg-hero.jpg` | Hero background image (to be added manually) |
| `db/schema.sql` | CREATE TABLE script for `inscricoes` |

---

## Task 1: Database Schema

**Files:**
- Create: `db/schema.sql`

- [ ] **Step 1: Create the SQL schema file**

```sql
-- db/schema.sql
CREATE TABLE IF NOT EXISTS `inscricoes` (
  `id`                 INT          NOT NULL AUTO_INCREMENT,
  `nome`               VARCHAR(255) NOT NULL,
  `email`              VARCHAR(255) NOT NULL,
  `loja_origem`        VARCHAR(255) NOT NULL,
  `evento`             VARCHAR(255) NOT NULL,
  `regiao`             ENUM('norte','sul') NOT NULL,
  `consentimento_mkt`  TINYINT(1)   NOT NULL DEFAULT 0,
  `email_enviado`      TINYINT(1)   NOT NULL DEFAULT 0,
  `data_inscricao`     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uq_email_regiao` (`email`, `regiao`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```

- [ ] **Step 2: Run the schema against your MySQL database**

```bash
mysql -u YOUR_USER -p YOUR_DATABASE < db/schema.sql
```

Expected: No errors, table `inscricoes` created.

- [ ] **Step 3: Verify the table**

```sql
DESCRIBE inscricoes;
```

Expected: 9 columns — id, nome, email, loja_origem, evento, regiao, consentimento_mkt, email_enviado, data_inscricao.

- [ ] **Step 4: Commit**

```bash
git add db/schema.sql
git commit -m "feat: add inscricoes table schema"
```

---

## Task 2: config.php

**Files:**
- Create: `config.php`

- [ ] **Step 1: Create config.php with DB credentials, SMTP credentials, and region data**

```php
<?php
// config.php

// Database
define('DB_HOST', 'localhost');
define('DB_NAME', 'YOUR_DATABASE');
define('DB_USER', 'YOUR_USER');
define('DB_PASS', 'YOUR_PASSWORD');
define('DB_CHARSET', 'utf8mb4');

// SMTP (PHPMailer)
define('SMTP_HOST',     'smtp.example.com');
define('SMTP_USER',     'noreply@dyrup.pt');
define('SMTP_PASS',     'YOUR_SMTP_PASSWORD');
define('SMTP_PORT',     587);
define('SMTP_FROM',     'noreply@dyrup.pt');
define('SMTP_FROM_NAME','Equipa PPG Dyrup');

// Region data
$eventos = [
    'norte' => 'Quinta da Releta, Pintão — 16 Maio, 09h00–15h00',
    'sul'   => 'Quinta das Lezirias, Ómoura — 23 Maio, 09h00–15h00',
];

$lojas = [
    'norte' => [
        'Loja Norte 1', // placeholder — substituir pela lista real
        'Loja Norte 2',
    ],
    'sul' => [
        'Loja Sul 1',   // placeholder — substituir pela lista real
        'Loja Sul 2',
    ],
];
```

- [ ] **Step 2: Fill in real DB and SMTP credentials**

Replace all placeholder values (YOUR_DATABASE, YOUR_USER, etc.) with the actual credentials for the server.

- [ ] **Step 3: Add config.php to .gitignore**

```bash
echo "config.php" >> .gitignore
```

- [ ] **Step 4: Commit .gitignore (not config.php)**

```bash
git add .gitignore
git commit -m "chore: ignore config.php to protect credentials"
```

---

## Task 3: send_email.php

**Files:**
- Create: `send_email.php`

- [ ] **Step 1: Create send_email.php**

```php
<?php
// send_email.php

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require_once __DIR__ . '/PHPMailer/src/Exception.php';
require_once __DIR__ . '/PHPMailer/src/PHPMailer.php';
require_once __DIR__ . '/PHPMailer/src/SMTP.php';
require_once __DIR__ . '/config.php';

/**
 * Envia email de confirmação de inscrição.
 *
 * @param string $nome   Nome do inscrito
 * @param string $email  Email do inscrito
 * @param string $evento String do evento (ex: "Quinta da Releta, Pintão — 16 Maio, 09h00–15h00")
 * @return bool          true se enviado com sucesso, false em caso de erro
 */
function enviar_confirmacao(string $nome, string $email, string $evento): bool
{
    $mail = new PHPMailer(true);
    try {
        $mail->isSMTP();
        $mail->Host       = SMTP_HOST;
        $mail->SMTPAuth   = true;
        $mail->Username   = SMTP_USER;
        $mail->Password   = SMTP_PASS;
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port       = SMTP_PORT;
        $mail->CharSet    = 'UTF-8';

        $mail->setFrom(SMTP_FROM, SMTP_FROM_NAME);
        $mail->addAddress($email, $nome);

        $mail->Subject = 'Confirmação da sua inscrição – Academia PPG Dyrup 2026';
        $mail->Body    = "Caro(a) {$nome},\n\n"
            . "Obrigado pela sua inscrição na Academia PPG Dyrup 2026.\n"
            . "A sua presença está confirmada e contamos consigo para um dia cheio de conhecimento, partilha e momentos especiais.\n\n"
            . "Data e local: {$evento}\n\n"
            . "Até breve,\n"
            . "Equipa PPG Dyrup";

        $mail->send();
        return true;
    } catch (Exception $e) {
        error_log('PHPMailer error: ' . $mail->ErrorInfo);
        return false;
    }
}
```

- [ ] **Step 2: Verify PHPMailer files exist at the expected paths**

```bash
ls PHPMailer/src/PHPMailer.php PHPMailer/src/SMTP.php PHPMailer/src/Exception.php
```

Expected: all three files listed. If the directory name differs, adjust the `require_once` paths in `send_email.php`.

- [ ] **Step 3: Commit**

```bash
git add send_email.php
git commit -m "feat: add enviar_confirmacao() email function"
```

---

## Task 4: index.php — PHP logic

**Files:**
- Create: `index.php`

- [ ] **Step 1: Create index.php with PHP logic at the top (no HTML yet)**

```php
<?php
// index.php

session_start();
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/send_email.php';

$regioes_validas = ['norte', 'sul'];
$regiao = $_GET['regiao'] ?? '';
$regiao_valida = in_array($regiao, $regioes_validas, true);

$evento   = $regiao_valida ? $eventos[$regiao] : '';
$lojas_regiao = $regiao_valida ? $lojas[$regiao]  : [];

$sucesso    = false;
$erro       = '';
$erro_duplicado = false;
$form_data  = [];

// CSRF token generation
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['csrf_token'];

if ($_SERVER['REQUEST_METHOD'] === 'POST' && $regiao_valida) {

    // CSRF check
    if (!hash_equals($csrf_token, $_POST['csrf_token'] ?? '')) {
        $erro = 'Pedido inválido. Por favor recarregue a página e tente novamente.';
    } else {
        $nome        = trim($_POST['nome']        ?? '');
        $email_input = trim($_POST['email']       ?? '');
        $loja        = trim($_POST['loja_origem'] ?? '');
        $consentimento = isset($_POST['consentimento_mkt']) ? 1 : 0;

        $form_data = [
            'nome'  => $nome,
            'email' => $email_input,
            'loja'  => $loja,
        ];

        // Validation
        if ($nome === '' || $email_input === '' || $loja === '') {
            $erro = 'Por favor preencha todos os campos obrigatórios.';
        } elseif (!filter_var($email_input, FILTER_VALIDATE_EMAIL)) {
            $erro = 'Por favor introduza um endereço de email válido.';
        } elseif (!in_array($loja, $lojas_regiao, true)) {
            $erro = 'Por favor selecione uma loja válida.';
        } else {
            // Database
            try {
                $dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=' . DB_CHARSET;
                $pdo = new PDO($dsn, DB_USER, DB_PASS, [
                    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                ]);

                // Check for duplicate
                $stmt = $pdo->prepare('SELECT id FROM inscricoes WHERE email = :email AND regiao = :regiao LIMIT 1');
                $stmt->execute([':email' => $email_input, ':regiao' => $regiao]);
                if ($stmt->fetch()) {
                    $erro = 'Este email já está inscrito neste evento.';
                    $erro_duplicado = true;
                } else {
                    // Insert first, then send email — avoids inconsistent state
                    // (email sent but no DB record) if one operation fails
                    $insert = $pdo->prepare(
                        'INSERT INTO inscricoes (nome, email, loja_origem, evento, regiao, consentimento_mkt, email_enviado)
                         VALUES (:nome, :email, :loja, :evento, :regiao, :consentimento, 0)'
                    );
                    $insert->execute([
                        ':nome'         => $nome,
                        ':email'        => $email_input,
                        ':loja'         => $loja,
                        ':evento'       => $evento,
                        ':regiao'       => $regiao,
                        ':consentimento'=> $consentimento,
                    ]);
                    $new_id = $pdo->lastInsertId();
                    // Send email and update email_enviado flag
                    $email_enviado = enviar_confirmacao($nome, $email_input, $evento) ? 1 : 0;
                    $pdo->prepare('UPDATE inscricoes SET email_enviado = :sent WHERE id = :id')
                        ->execute([':sent' => $email_enviado, ':id' => $new_id]);
                    $sucesso = true;
                    // Regenerate CSRF token after successful submit
                    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
                }
            } catch (PDOException $e) {
                error_log('DB error: ' . $e->getMessage());
                $erro = 'Ocorreu um erro ao processar a sua inscrição. Por favor tente novamente.';
            }
        }
    }
}
?>
```

- [ ] **Step 2: Verify PHP parses without errors**

```bash
php -l index.php
```

Expected: `No syntax errors detected in index.php`

- [ ] **Step 3: Commit PHP logic**

```bash
git add index.php
git commit -m "feat: add PHP logic to index.php (validation, DB, email)"
```

---

## Task 5: index.php — HTML template

**Files:**
- Modify: `index.php` (append HTML after PHP block)

- [ ] **Step 1: Append the HTML template to index.php**

```php
<!DOCTYPE html>
<html lang="pt">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>A Magia da Cor – Academia PPG Dyrup 2026</title>
    <link rel="stylesheet" href="assets/style.css">
</head>
<body>

<?php if (!$regiao_valida): ?>
    <!-- Invalid/missing region: show logo only -->
    <div class="logo-only">
        <img src="assets/logo-dyrup.png" alt="Dyrup" class="logo-only__img">
    </div>

<?php else: ?>

    <!-- HERO -->
    <header class="hero">
        <img src="assets/logo-dyrup.png" alt="Dyrup" class="hero__logo">
        <h1 class="hero__title">
            <span class="hero__title-a">A</span>
            <span class="hero__title-magia">Magia</span>
            <span class="hero__title-da">da</span>
            <span class="hero__title-cor">C<span class="hero__title-dot">·</span>or</span>
        </h1>
    </header>

    <!-- CONTENT -->
    <main class="content">
        <div class="content__inner">
            <p>A Academia PPG Dyrup está de volta e este ano traz consigo um dia especial dedicado à Magia das Cores. Junte-se a nós para descobrir truques, técnicas e inovações que dão vida aos seus projetos.</p>
            <p>O programa inclui 5 postos técnicos, momentos de convívio, almoço volante e muita cor para celebrarmos juntos o mundo das tintas.</p>
            <p class="content__aviso"><strong>Para melhor organização do evento, agradecemos que a inscrição seja feita apenas caso esteja seguro(a) da sua presença.</strong></p>

            <?php if ($sucesso): ?>
                <div class="form-success">
                    <p>A sua inscrição foi registada com sucesso!</p>
                    <p>Irá receber um email de confirmação em breve.</p>
                </div>
            <?php else: ?>

                <?php if ($erro): ?>
                    <div class="form-error"><?= htmlspecialchars($erro) ?></div>
                <?php endif; ?>

                <form class="form" method="POST" action="?regiao=<?= htmlspecialchars($regiao) ?>">
                    <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">

                    <div class="form__row">
                        <input
                            type="text"
                            name="nome"
                            placeholder="Nome completo"
                            class="form__input form__input--full"
                            value="<?= htmlspecialchars($form_data['nome'] ?? '') ?>"
                            required>
                    </div>

                    <div class="form__row form__row--two">
                        <input
                            type="email"
                            name="email"
                            placeholder="Email"
                            class="form__input"
                            value="<?= htmlspecialchars($form_data['email'] ?? '') ?>"
                            required>

                        <select name="loja_origem" class="form__select" required>
                            <option value="" disabled <?= empty($form_data['loja']) ? 'selected' : '' ?>>Loja de origem</option>
                            <?php foreach ($lojas_regiao as $loja): ?>
                                <option value="<?= htmlspecialchars($loja) ?>"
                                    <?= (($form_data['loja'] ?? '') === $loja) ? 'selected' : '' ?>>
                                    <?= htmlspecialchars($loja) ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                    </div>

                    <div class="form__row">
                        <input
                            type="text"
                            class="form__input form__input--full form__input--locked"
                            value="<?= htmlspecialchars($evento) ?>"
                            readonly>
                        <input type="hidden" name="evento" value="<?= htmlspecialchars($evento) ?>">
                    </div>

                    <div class="form__row form__row--checkbox">
                        <label class="form__checkbox-label">
                            <input type="checkbox" name="consentimento_mkt" value="1">
                            Autorizo o envio de comunicações de marketing do PPG Dyrup por email/SMS.
                            Os dados fornecidos neste formulário não serão armazenados nem utilizados para qualquer outro fim.
                        </label>
                    </div>

                    <button type="submit" class="form__submit">Confirmar inscrição</button>

                    <p class="form__privacy">
                        Pode cancelar o consentimento a qualquer momento <a href="#">aqui</a>.
                        Consulte a <a href="#">Política de Privacidade</a>.
                    </p>
                </form>

            <?php endif; ?>
        </div>
    </main>

<?php endif; ?>

</body>
</html>
```

- [ ] **Step 2: Check PHP parses cleanly**

```bash
php -l index.php
```

Expected: `No syntax errors detected in index.php`

- [ ] **Step 3: Open in browser and verify the three states render**

Test URLs (adjust base URL to your local server):
- `http://localhost/amagiadacor/` → logo only on dark background
- `http://localhost/amagiadacor/?regiao=norte` → full page, Norte event pre-filled
- `http://localhost/amagiadacor/?regiao=sul` → full page, Sul event pre-filled

- [ ] **Step 4: Commit**

```bash
git add index.php
git commit -m "feat: add HTML template to index.php"
```

---

## Task 6: assets/style.css

**Files:**
- Create: `assets/style.css`

- [ ] **Step 1: Create the stylesheet**

```css
/* assets/style.css */

*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Arial', sans-serif;
    background: #1a0a2e;
    color: #fff;
    min-height: 100vh;
}

/* ── LOGO ONLY (invalid region) ── */
.logo-only {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: linear-gradient(160deg, #1a0a2e 0%, #3d0a4f 50%, #1a0a2e 100%);
}

.logo-only__img {
    max-width: 200px;
    width: 60%;
}

/* ── HERO ── */
.hero {
    position: relative;
    background: linear-gradient(160deg, #1a0a2e 0%, #3d0a4f 50%, #1a0a2e 100%);
    padding: 40px 20px 80px;
    text-align: center;
    overflow: hidden;
}

/* Star/sparkle effect via pseudo-element */
.hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        radial-gradient(circle, rgba(255,220,80,0.8) 1px, transparent 1px),
        radial-gradient(circle, rgba(255,220,80,0.5) 1px, transparent 1px),
        radial-gradient(circle, rgba(255,255,255,0.6) 1px, transparent 1px);
    background-size: 120px 120px, 80px 80px, 60px 60px;
    background-position: 10px 20px, 40px 60px, 70px 10px;
    pointer-events: none;
}

.hero__logo {
    width: 120px;
    position: relative;
    z-index: 1;
    margin-bottom: 24px;
}

.hero__title {
    position: relative;
    z-index: 1;
    font-size: clamp(48px, 12vw, 110px);
    line-height: 0.9;
    font-style: italic;
    font-weight: 900;
    color: #f5c800;
    text-shadow: 2px 2px 0 #c98a00, 0 0 30px rgba(245,200,0,0.4);
    letter-spacing: -2px;
}

.hero__title-da {
    font-size: 0.45em;
    display: inline-block;
    vertical-align: middle;
}

.hero__title-dot {
    color: #fff;
}

/* ── CONTENT ── */
.content {
    background: linear-gradient(180deg, #c2185b 0%, #ad1457 100%);
    padding: 48px 20px 60px;
}

.content__inner {
    max-width: 680px;
    margin: 0 auto;
}

.content__inner p {
    font-size: 15px;
    line-height: 1.6;
    margin-bottom: 16px;
    color: #fff;
}

.content__aviso {
    margin-bottom: 32px !important;
}

/* ── FORM ── */
.form {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.form__row {
    display: flex;
    gap: 12px;
}

.form__row--two {
    flex-wrap: wrap;
}

.form__row--two > * {
    flex: 1 1 200px;
    min-width: 0;
}

.form__input,
.form__select {
    width: 100%;
    padding: 12px 16px;
    border: 1px solid rgba(255,255,255,0.4);
    border-radius: 4px;
    background: rgba(255,255,255,0.15);
    color: #fff;
    font-size: 14px;
    outline: none;
    transition: border-color 0.2s;
}

.form__input::placeholder {
    color: rgba(255,255,255,0.7);
}

.form__input:focus,
.form__select:focus {
    border-color: #fff;
    background: rgba(255,255,255,0.25);
}

.form__input--full {
    flex: 1 1 100%;
}

.form__input--locked {
    cursor: default;
    opacity: 0.85;
}

.form__select {
    appearance: none;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='white' d='M6 8L0 0h12z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
    padding-right: 36px;
}

.form__select option {
    background: #ad1457;
    color: #fff;
}

.form__row--checkbox {
    align-items: flex-start;
}

.form__checkbox-label {
    font-size: 12px;
    color: rgba(255,255,255,0.85);
    line-height: 1.5;
    cursor: pointer;
    display: flex;
    gap: 8px;
    align-items: flex-start;
}

.form__checkbox-label input[type="checkbox"] {
    margin-top: 2px;
    flex-shrink: 0;
    accent-color: #f5c800;
}

.form__submit {
    background: linear-gradient(90deg, #f5a623 0%, #e8372b 100%);
    color: #fff;
    border: none;
    border-radius: 4px;
    padding: 14px 28px;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    transition: opacity 0.2s;
    align-self: flex-start;
}

.form__submit:hover {
    opacity: 0.9;
}

.form__privacy {
    font-size: 11px;
    color: rgba(255,255,255,0.75);
    line-height: 1.5;
    margin-top: 4px !important;
}

.form__privacy a {
    color: #fff;
    text-decoration: underline;
}

/* ── FEEDBACK STATES ── */
.form-success {
    background: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.5);
    border-radius: 6px;
    padding: 24px;
    text-align: center;
    font-size: 16px;
    line-height: 1.6;
}

.form-error {
    background: rgba(200,0,0,0.3);
    border: 1px solid rgba(255,100,100,0.6);
    border-radius: 6px;
    padding: 12px 16px;
    font-size: 14px;
    margin-bottom: 8px;
}

/* ── RESPONSIVE ── */
@media (max-width: 480px) {
    .form__row--two {
        flex-direction: column;
    }

    .form__submit {
        align-self: stretch;
        text-align: center;
    }
}
```

- [ ] **Step 2: Add logo and background assets to assets/ folder**

Copy `logo-dyrup.png` and `bg-hero.jpg` (if available as a separate image) into `assets/`. If no background image exists, the CSS gradient will serve as the hero background.

- [ ] **Step 3: Open in browser and verify visual fidelity against mockup**

Check:
- Hero: dark gradient with sparkle dots, logo at top, large golden italic title
- Content: pink/magenta background, white text, form fields styled correctly
- Invalid region URL: only logo centered on dark background

- [ ] **Step 4: Commit**

```bash
git add assets/style.css
git commit -m "feat: add landing page styles"
```

---

## Task 7: End-to-end manual test

- [ ] **Step 1: Test invalid region state**

Navigate to `?regiao=` (empty) and `?regiao=xyz`.
Expected: Only logo visible on dark background, no form.

- [ ] **Step 2: Test Norte region**

Navigate to `?regiao=norte`.
Expected: Full page, event shows "Quinta da Releta, Pintão — 16 Maio, 09h00–15h00", store dropdown shows Norte stores.

- [ ] **Step 3: Test Sul region**

Navigate to `?regiao=sul`.
Expected: Full page, event shows "Quinta das Lezirias, Ómoura — 23 Maio, 09h00–15h00", store dropdown shows Sul stores.

- [ ] **Step 4: Test validation errors**

Submit with empty fields. Expected: inline error message, form stays visible.
Submit with invalid email format. Expected: inline email error.

- [ ] **Step 5: Test successful submission**

Submit a valid form (Norte). Expected:
- Record appears in `inscricoes` table with correct `regiao`, `evento`, `email_enviado`
- Confirmation email received at the submitted address
- Form replaced by success message

- [ ] **Step 6: Test duplicate email**

Submit the same email for the same region again.
Expected: inline error "Este email já está inscrito neste evento.", form stays visible.

- [ ] **Step 7: Commit final state**

```bash
git add -A
git commit -m "feat: complete A Magia da Cor landing page"
```
