Portfólio dos Projetos

Nesse tutorial vamos criar a página do seu portfólio para apresentar seus projetos:

1- Acesse a sua hospedagem Infinityfree e clique em “Manager”

2- Acesse “Flie Manager”

3- Dentro da pasta raiz “Htdocs” crie as pastas img e avatar, deixe essas pastas vazias serão usadas para guardas as imagens.

4- Ainda dentro de Htdocs, cries os outros arquivos conforme a estrutura abaixo:

htdocs/
├── avatar/                 ← (nova pasta)Foto de perfil do aluno
├── img/                    ← (nova pasta) Imagens dos projetos
├── index.php               ← (arquivo) Portfólio público
├── style.css               ← (arquivo) Estilo dinâmico (usa as cores salvas)
├── config.php              ← (arquivo) Configuração + funções
├── dados.json              ← (arquivo) Dados do aluno + projetos (gerado)
├── login.php               ← (arquivo) Login do dashboard
├── logout.php              ← (arquivo) Sair
├── dashboard.php           ← (arquivo) Painel principal
├── projeto_form.php        ← (arquivo) Adicionar/editar projeto
├── projeto_excluir.php     ← (arquivo) Excluir projeto
├── perfil.php              ← (arquivo) Editar dados do aluno
├── admin.css               ← (arquivo) Estilo do dashboard

Definir a senha de acesso

No arquivo config.php contém a senha. Padrão: admin123 (pode trocar).

Arquivo 1: config.php

<?php
// config.php - Configuração geral + funções

// ==========================================================
// SENHA DO DASHBOARD (altere para a sua)
// ==========================================================
define('SENHA_DASHBOARD', 'admin123');

// Caminhos
define('ARQ_DADOS', __DIR__ . '/dados.json');
define('UPLOAD_IMG_DIR', __DIR__ . '/img/');
define('UPLOAD_AVATAR_DIR', __DIR__ . '/avatar/');
define('UPLOAD_IMG_URL', 'img/');
define('UPLOAD_AVATAR_URL', 'avatar/');

// Inicia sessão
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

// ==========================================================
// CARREGA DADOS
// ==========================================================
function carregarDados() {
    if (!file_exists(ARQ_DADOS)) {
        // Dados padrão na primeira execução
        return [
            'perfil' => [
                'nome'   => 'Seu Nome Aqui',
                'turma'  => '2º TEC 01',
                'bio'    => 'Estudante do curso Técnico em Desenvolvimento de Sistemas.',
                'avatar' => '',
                'email'  => '',
                'github' => '',
                'linkedin' => ''
            ],
            'tema' => [
                'cor_primaria'  => '#0f2b46',
                'cor_secundaria'=> '#c9a227',
                'cor_fundo'     => '#f4f6f9'
            ],
            'projetos' => []
        ];
    }

    $conteudo = file_get_contents(ARQ_DADOS);
    $dados = json_decode($conteudo, true);

    // Se der erro no JSON, retorna padrão
    if (!is_array($dados)) {
        return [
            'perfil' => ['nome' => 'Aluno', 'turma' => '', 'bio' => '', 'avatar' => '', 'email' => '', 'github' => '', 'linkedin' => ''],
            'tema' => ['cor_primaria' => '#0f2b46', 'cor_secundaria' => '#c9a227', 'cor_fundo' => '#f4f6f9'],
            'projetos' => []
        ];
    }

    // Garante que as chaves existem
    $dados['perfil']   = $dados['perfil']   ?? [];
    $dados['tema']     = $dados['tema']     ?? ['cor_primaria' => '#0f2b46', 'cor_secundaria' => '#c9a227', 'cor_fundo' => '#f4f6f9'];
    $dados['projetos'] = $dados['projetos'] ?? [];

    return $dados;
}

function salvarDados($dados) {
    file_put_contents(
        ARQ_DADOS,
        json_encode($dados, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
        LOCK_EX
    );
}

// ==========================================================
// AUTENTICAÇÃO
// ==========================================================
function estaLogado() {
    return isset($_SESSION['admin_logado']) && $_SESSION['admin_logado'] === true;
}

function exigirLogin() {
    if (!estaLogado()) {
        header('Location: login.php');
        exit;
    }
}

// ==========================================================
// UPLOAD
// ==========================================================
function fazerUpload($arquivo, $dir, $prefixo) {
    if (empty($arquivo['name']) || $arquivo['error'] !== UPLOAD_ERR_OK) {
        return ['ok' => false, 'msg' => 'Nenhum arquivo enviado'];
    }

    $ext = strtolower(pathinfo($arquivo['name'], PATHINFO_EXTENSION));
    $permitidas = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];

    if (!in_array($ext, $permitidas)) {
        return ['ok' => false, 'msg' => 'Formato inválido. Use JPG, PNG, GIF, WEBP ou SVG.'];
    }

    if ($arquivo['size'] > 5 * 1024 * 1024) {
        return ['ok' => false, 'msg' => 'Imagem muito grande (máx. 5MB).'];
    }

    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }

    $nome = uniqid($prefixo) . '.' . $ext;
    $caminho = $dir . $nome;

    if (!move_uploaded_file($arquivo['tmp_name'], $caminho)) {
        return ['ok' => false, 'msg' => 'Erro ao salvar arquivo.'];
    }

    return ['ok' => true, 'nome' => $nome];
}

// ==========================================================
// GERA SLUG (nome de pasta amigável)
// ==========================================================
function gerarSlug($texto) {
    $texto = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $texto);
    $texto = strtolower(trim($texto));
    $texto = preg_replace('/[^a-z0-9]+/', '-', $texto);
    return trim($texto, '-');
}
?>

Arquivo 2: login.php

<?php
require_once 'config.php';

if (estaLogado()) {
    header('Location: dashboard.php');
    exit;
}

$erro = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $senha = $_POST['senha'] ?? '';

    if ($senha === SENHA_DASHBOARD) {
        $_SESSION['admin_logado'] = true;
        header('Location: dashboard.php');
        exit;
    } else {
        $erro = 'Senha incorreta';
    }
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <title>Login - Gerenciar Portfólio</title>
    <link rel="stylesheet" href="admin.css">
</head>
<body class="login-body">
    <div class="login-card">
        <div class="login-icon">🔐</div>
        <h1>Acesso Restrito</h1>
        <p class="login-sub">Área de gerenciamento do portfólio</p>

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

        <form method="POST">
            <div class="form-group">
                <label>Senha</label>
                <input type="password" name="senha" required autofocus>
            </div>
            <button type="submit" class="btn-full">🔓 Entrar</button>
        </form>

        <a href="index.php" class="login-back">← Voltar ao portfólio</a>
    </div>
</body>
</html>

Arquivo 3: logout.php

<?php
require_once 'config.php';
session_destroy();
header('Location: index.php');
exit;
?>

Arquivo 4: dashboard.php

<?php
require_once 'config.php';
exigirLogin();

$dados = carregarDados();
$projetos = $dados['projetos'];
$perfil = $dados['perfil'];
$tema = $dados['tema'];

$msg = $_GET['msg'] ?? '';
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard - Meu Portfólio</title>
    <link rel="stylesheet" href="admin.css">
</head>
<body>
    <header class="topbar">
        <div class="topbar-content">
            <div class="topbar-brand">
                <span>🎨</span>
                <div>
                    <strong>Meu Portfólio</strong>
                    <small>Gerenciamento</small>
                </div>
            </div>
            <nav class="topbar-nav">
                <a href="index.php" target="_blank">👁️ Ver Portfólio</a>
                <a href="perfil.php">👤 Perfil</a>
                <a href="logout.php" class="btn-sair">🚪 Sair</a>
            </nav>
        </div>
    </header>

    <main class="container">
        <?php if ($msg === 'add'): ?>
            <div class="alert alert-success">✅ Projeto adicionado com sucesso!</div>
        <?php elseif ($msg === 'edit'): ?>
            <div class="alert alert-success">✅ Projeto atualizado!</div>
        <?php elseif ($msg === 'del'): ?>
            <div class="alert alert-success">🗑️ Projeto excluído.</div>
        <?php elseif ($msg === 'perfil'): ?>
            <div class="alert alert-success">✅ Perfil atualizado!</div>
        <?php endif; ?>

        <!-- Info do perfil -->
        <section class="perfil-resumo">
            <div class="perfil-avatar">
                <?php if (!empty($perfil['avatar']) && file_exists(UPLOAD_AVATAR_DIR . $perfil['avatar'])): ?>
                    <img src="<?php echo UPLOAD_AVATAR_URL . htmlspecialchars($perfil['avatar']); ?>" alt="Avatar">
                <?php else: ?>
                    <span><?php echo strtoupper(substr($perfil['nome'] ?? 'A', 0, 1)); ?></span>
                <?php endif; ?>
            </div>
            <div class="perfil-info">
                <h2><?php echo htmlspecialchars($perfil['nome'] ?? 'Seu Nome'); ?></h2>
                <p><?php echo htmlspecialchars($perfil['turma'] ?? ''); ?></p>
            </div>
            <a href="perfil.php" class="btn-perfil">✏️ Editar Perfil</a>
        </section>

        <!-- Cabeçalho da lista -->
        <div class="lista-header">
            <div>
                <h1>Meus Projetos</h1>
                <p class="lista-sub"><?php echo count($projetos); ?> projeto(s) cadastrado(s)</p>
            </div>
            <a href="projeto_form.php" class="btn-novo">➕ Adicionar Projeto</a>
        </div>

        <!-- Lista -->
        <?php if (empty($projetos)): ?>
            <div class="empty-state">
                <span class="empty-icon">📭</span>
                <h2>Você ainda não tem projetos</h2>
                <p>Adicione seu primeiro projeto para exibir no portfólio.</p>
                <a href="projeto_form.php" class="btn-novo">➕ Adicionar Projeto</a>
            </div>
        <?php else: ?>
            <div class="grid-projetos">
                <?php foreach ($projetos as $p): ?>
                    <div class="card-admin">
                        <div class="card-img">
                            <?php if (!empty($p['imagem']) && file_exists(UPLOAD_IMG_DIR . $p['imagem'])): ?>
                                <img src="<?php echo UPLOAD_IMG_URL . htmlspecialchars($p['imagem']); ?>" alt="">
                            <?php else: ?>
                                <div class="card-noimg">💻</div>
                            <?php endif; ?>
                        </div>
                        <div class="card-body">
                            <h3><?php echo htmlspecialchars($p['titulo']); ?></h3>
                            <?php if (!empty($p['descricao'])): ?>
                                <p class="card-desc">
                                    <?php echo htmlspecialchars(mb_strimwidth($p['descricao'], 0, 100, '...')); ?>
                                </p>
                            <?php endif; ?>
                            <div class="card-actions">
                                <a href="projeto_form.php?id=<?php echo $p['id']; ?>" class="btn-mini btn-edit">✏️</a>
                                <a href="projeto_excluir.php?id=<?php echo $p['id']; ?>" 
                                   class="btn-mini btn-del"
                                   onclick="return confirm('Excluir este projeto?')">🗑️</a>
                            </div>
                        </div>
                    </div>
                <?php endforeach; ?>
            </div>
        <?php endif; ?>
    </main>
</body>
</html>

Arquivo 5: projeto_form.php (Adicionar/Editar)

<?php
require_once 'config.php';
exigirLogin();

$dados = carregarDados();
$erros = [];
$editando = false;
$id = isset($_GET['id']) ? trim($_GET['id']) : '';

// Dados iniciais do formulário
$projeto = [
    'id' => '',
    'titulo' => '',
    'descricao' => '',
    'imagem' => '',
    'link' => '',
    'tecnologias' => '',
    'pasta' => ''
];

// Se está editando, carrega o projeto
if ($id !== '') {
    foreach ($dados['projetos'] as $p) {
        if ($p['id'] === $id) {
            $projeto = $p;
            $editando = true;
            break;
        }
    }
    if (!$editando) {
        header('Location: dashboard.php');
        exit;
    }
}

// Processa o formulário
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $projeto['titulo']      = trim($_POST['titulo'] ?? '');
    $projeto['descricao']   = trim($_POST['descricao'] ?? '');
    $projeto['link']        = trim($_POST['link'] ?? '');
    $projeto['tecnologias'] = trim($_POST['tecnologias'] ?? '');
    $projeto['pasta']       = trim($_POST['pasta'] ?? '');

    if (strlen($projeto['titulo']) < 3) $erros[] = 'Título muito curto';
    if (empty($projeto['link']) || !filter_var($projeto['link'], FILTER_VALIDATE_URL)) {
        $erros[] = 'Link inválido (comece com http:// ou https://)';
    }
    if (empty($projeto['pasta'])) {
        $erros[] = 'Informe a pasta do projeto (ex: conversor)';
    } elseif (!preg_match('/^[a-zA-Z0-9_\-]+$/', $projeto['pasta'])) {
        $erros[] = 'Nome da pasta só pode ter letras, números, _ e -';
    }

    // Upload de imagem (opcional)
    $nova_imagem = null;
    if (!empty($_FILES['imagem']['name'])) {
        $resultado = fazerUpload($_FILES['imagem'], UPLOAD_IMG_DIR, 'proj_');
        if ($resultado['ok']) {
            $nova_imagem = $resultado['nome'];
        } else {
            $erros[] = $resultado['msg'];
        }
    }

    if (empty($erros)) {
        // Remove imagem antiga se trocou
        if ($nova_imagem && !empty($projeto['imagem']) && file_exists(UPLOAD_IMG_DIR . $projeto['imagem'])) {
            unlink(UPLOAD_IMG_DIR . $projeto['imagem']);
        }

        $imagem_final = $nova_imagem ?: $projeto['imagem'];

        if ($editando) {
            // Atualiza o projeto existente (comparação por string)
            foreach ($dados['projetos'] as &$p) {
                if ($p['id'] === $id) {
                    $p['titulo']      = $projeto['titulo'];
                    $p['descricao']   = $projeto['descricao'];
                    $p['link']        = $projeto['link'];
                    $p['tecnologias'] = $projeto['tecnologias'];
                    $p['pasta']       = $projeto['pasta'];
                    $p['imagem']      = $imagem_final;
                    break;
                }
            }
            unset($p);
            salvarDados($dados);
            header('Location: dashboard.php?msg=edit');
            exit;
        } else {
            // Adiciona novo projeto
            $dados['projetos'][] = [
                'id'          => uniqid(),
                'titulo'      => $projeto['titulo'],
                'descricao'   => $projeto['descricao'],
                'link'        => $projeto['link'],
                'tecnologias' => $projeto['tecnologias'],
                'pasta'       => $projeto['pasta'],
                'imagem'      => $imagem_final
            ];
            salvarDados($dados);
            header('Location: dashboard.php?msg=add');
            exit;
        }
    }
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <title><?php echo $editando ? 'Editar' : 'Novo'; ?> Projeto</title>
    <link rel="stylesheet" href="admin.css">
</head>
<body>
    <header class="topbar">
        <div class="topbar-content">
            <div class="topbar-brand">
                <span><?php echo $editando ? '✏️' : '➕'; ?></span>
                <div>
                    <strong><?php echo $editando ? 'Editar' : 'Novo'; ?> Projeto</strong>
                    <small>Gerenciar portfólio</small>
                </div>
            </div>
            <nav class="topbar-nav">
                <a href="dashboard.php">← Voltar</a>
            </nav>
        </div>
    </header>

    <main class="container container-narrow">
        <?php if (!empty($erros)): ?>
            <div class="alert alert-error">
                <?php foreach ($erros as $e): ?>
                    <div>⚠️ <?php echo htmlspecialchars($e); ?></div>
                <?php endforeach; ?>
            </div>
        <?php endif; ?>

        <form method="POST" enctype="multipart/form-data" class="form-card">
            <div class="form-group">
                <label>📌 Título do projeto *</label>
                <input type="text" name="titulo" required maxlength="150"
                       value="<?php echo htmlspecialchars($projeto['titulo']); ?>"
                       placeholder="Ex: Sistema de Conversor de Moedas">
            </div>

            <div class="form-group">
                <label>📁 Nome da pasta no servidor *</label>
                <input type="text" name="pasta" required maxlength="80"
                       value="<?php echo htmlspecialchars($projeto['pasta']); ?>"
                       placeholder="Ex: conversor">
                <small>O nome exato da pasta onde está o projeto. Sem barras, sem espaços.</small>
            </div>

            <div class="form-group">
                <label>📝 Descrição</label>
                <textarea name="descricao" rows="4"
                          placeholder="Descreva brevemente o projeto..."><?php echo htmlspecialchars($projeto['descricao']); ?></textarea>
            </div>

            <div class="form-group">
                <label>🖼️ Imagem (print do sistema)</label>
                <?php if ($editando && !empty($projeto['imagem']) && file_exists(UPLOAD_IMG_DIR . $projeto['imagem'])): ?>
                    <img src="<?php echo UPLOAD_IMG_URL . htmlspecialchars($projeto['imagem']); ?>" 
                         class="preview-img">
                <?php endif; ?>
                <input type="file" name="imagem" accept="image/*">
                <small>Formatos: JPG, PNG, GIF, WEBP. Máximo 5MB.</small>
            </div>

            <div class="form-group">
                <label>🔗 Link do projeto *</label>
                <input type="url" name="link" required
                       value="<?php echo htmlspecialchars($projeto['link']); ?>"
                       placeholder="https://seu-site.com/conversor/">
                <small>Cole a URL completa. Você pode usar sua própria hospedagem ou a URL do InfinityFree.</small>
            </div>

            <div class="form-group">
                <label>💻 Tecnologias usadas</label>
                <input type="text" name="tecnologias"
                       value="<?php echo htmlspecialchars($projeto['tecnologias']); ?>"
                       placeholder="Ex: PHP, MySQL, JavaScript">
                <small>Separe por vírgula.</small>
            </div>

            <div class="form-actions">
                <a href="dashboard.php" class="btn-cancelar">Cancelar</a>
                <button type="submit" class="btn-salvar">💾 Salvar</button>
            </div>
        </form>
    </main>
</body>
</html>

Arquivo 6: projeto_excluir.php

<?php
require_once 'config.php';
exigirLogin();

$id = isset($_GET['id']) ? trim($_GET['id']) : '';
$dados = carregarDados();

$encontrado = false;

foreach ($dados['projetos'] as $key => $p) {
    if ($p['id'] === $id) {
        // Remove a imagem
        if (!empty($p['imagem']) && file_exists(UPLOAD_IMG_DIR . $p['imagem'])) {
            unlink(UPLOAD_IMG_DIR . $p['imagem']);
        }
        // Remove do array
        unset($dados['projetos'][$key]);
        $encontrado = true;
        break;
    }
}

// Reindexa o array
$dados['projetos'] = array_values($dados['projetos']);
salvarDados($dados);

header('Location: dashboard.php?msg=del');
exit;
?>

 

Arquivo 7: perfil.php (Personalização)

<?php
require_once 'config.php';
exigirLogin();

$dados = carregarDados();
$perfil = $dados['perfil'];
$tema = $dados['tema'];
$erros = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Dados básicos
    $perfil['nome']     = trim($_POST['nome'] ?? '');
    $perfil['turma']    = trim($_POST['turma'] ?? '');
    $perfil['bio']      = trim($_POST['bio'] ?? '');
    $perfil['email']    = trim($_POST['email'] ?? '');
    $perfil['github']   = trim($_POST['github'] ?? '');
    $perfil['linkedin'] = trim($_POST['linkedin'] ?? '');

    if (strlen($perfil['nome']) < 2) $erros[] = 'Nome muito curto';

    // Upload do avatar
    if (!empty($_FILES['avatar']['name'])) {
        $resultado = fazerUpload($_FILES['avatar'], UPLOAD_AVATAR_DIR, 'avatar_');
        if ($resultado['ok']) {
            // Remove o antigo
            if (!empty($perfil['avatar']) && file_exists(UPLOAD_AVATAR_DIR . $perfil['avatar'])) {
                unlink(UPLOAD_AVATAR_DIR . $perfil['avatar']);
            }
            $perfil['avatar'] = $resultado['nome'];
        } else {
            $erros[] = $resultado['msg'];
        }
    }

    // Tema (cores)
    $tema['cor_primaria']   = $_POST['cor_primaria']   ?? '#0f2b46';
    $tema['cor_secundaria'] = $_POST['cor_secundaria'] ?? '#c9a227';
    $tema['cor_fundo']      = $_POST['cor_fundo']      ?? '#f4f6f9';

    // Validação básica de hex
    foreach (['cor_primaria', 'cor_secundaria', 'cor_fundo'] as $k) {
        if (!preg_match('/^#[0-9a-fA-F]{6}$/', $tema[$k])) {
            $erros[] = "Cor inválida em $k";
        }
    }

    if (empty($erros)) {
        $dados['perfil'] = $perfil;
        $dados['tema'] = $tema;
        salvarDados($dados);
        header('Location: dashboard.php?msg=perfil');
        exit;
    }
}

// Temas prontos (paletas)
$paletas = [
    ['nome' => 'Institucional (Azul)',    'primaria' => '#0f2b46', 'secundaria' => '#c9a227', 'fundo' => '#f4f6f9'],
    ['nome' => 'Moderno (Roxo)',          'primaria' => '#6c5ce7', 'secundaria' => '#fd79a8', 'fundo' => '#f8f9fc'],
    ['nome' => 'Verde (Natureza)',        'primaria' => '#1f7a3f', 'secundaria' => '#f39c12', 'fundo' => '#f5f9f5'],
    ['nome' => 'Vermelho (Vibrante)',     'primaria' => '#c0392b', 'secundaria' => '#f1c40f', 'fundo' => '#fdf5f4'],
    ['nome' => 'Cinza (Minimalista)',     'primaria' => '#2c3e50', 'secundaria' => '#7f8c8d', 'fundo' => '#f7f7f7'],
    ['nome' => 'Rosa (Criativo)',         'primaria' => '#e84393', 'secundaria' => '#fdcb6e', 'fundo' => '#fdf6f9'],
    ['nome' => 'Azul Céu (Clean)',        'primaria' => '#0984e3', 'secundaria' => '#00b894', 'fundo' => '#f0f8ff'],
    ['nome' => 'Preto (Dark)',            'primaria' => '#1a1a1a', 'secundaria' => '#ffb800', 'fundo' => '#f0f0f0']
];
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <title>Perfil e Personalização</title>
    <link rel="stylesheet" href="admin.css">
</head>
<body>
    <header class="topbar">
        <div class="topbar-content">
            <div class="topbar-brand">
                <span>👤</span>
                <div>
                    <strong>Perfil e Personalização</strong>
                    <small>Suas informações e tema</small>
                </div>
            </div>
            <nav class="topbar-nav">
                <a href="dashboard.php">← Voltar</a>
            </nav>
        </div>
    </header>

    <main class="container container-narrow">
        <?php if (!empty($erros)): ?>
            <div class="alert alert-error">
                <?php foreach ($erros as $e): ?>
                    <div>⚠️ <?php echo htmlspecialchars($e); ?></div>
                <?php endforeach; ?>
            </div>
        <?php endif; ?>

        <form method="POST" enctype="multipart/form-data">

            <!-- SEÇÃO: DADOS PESSOAIS -->
            <section class="form-card">
                <h2 class="section-title">👤 Meus Dados</h2>

                <div class="perfil-upload">
                    <div class="perfil-avatar-grande">
                        <?php if (!empty($perfil['avatar']) && file_exists(UPLOAD_AVATAR_DIR . $perfil['avatar'])): ?>
                            <img src="<?php echo UPLOAD_AVATAR_URL . htmlspecialchars($perfil['avatar']); ?>" alt="">
                        <?php else: ?>
                            <span><?php echo strtoupper(substr($perfil['nome'] ?? 'A', 0, 1)); ?></span>
                        <?php endif; ?>
                    </div>
                    <div class="perfil-upload-info">
                        <label>Sua foto de perfil</label>
                        <input type="file" name="avatar" accept="image/*">
                        <small>Aparecerá no seu portfólio público.</small>
                    </div>
                </div>

                <div class="form-group">
                    <label>Nome completo *</label>
                    <input type="text" name="nome" required
                           value="<?php echo htmlspecialchars($perfil['nome'] ?? ''); ?>">
                </div>

                <div class="form-group">
                    <label>Turma</label>
                    <input type="text" name="turma"
                           value="<?php echo htmlspecialchars($perfil['turma'] ?? ''); ?>"
                           placeholder="Ex: 2º TEC 01">
                </div>

                <div class="form-group">
                    <label>Bio (sobre você)</label>
                    <textarea name="bio" rows="3"
                              placeholder="Fale um pouco sobre você..."><?php echo htmlspecialchars($perfil['bio'] ?? ''); ?></textarea>
                </div>

                <div class="form-group">
                    <label>E-mail (opcional)</label>
                    <input type="email" name="email"
                           value="<?php echo htmlspecialchars($perfil['email'] ?? ''); ?>">
                </div>

                <div class="grid-2">
                    <div class="form-group">
                        <label>GitHub (opcional)</label>
                        <input type="text" name="github"
                               value="<?php echo htmlspecialchars($perfil['github'] ?? ''); ?>"
                               placeholder="https://github.com/usuario">
                    </div>
                    <div class="form-group">
                        <label>LinkedIn (opcional)</label>
                        <input type="text" name="linkedin"
                               value="<?php echo htmlspecialchars($perfil['linkedin'] ?? ''); ?>"
                               placeholder="https://linkedin.com/in/usuario">
                    </div>
                </div>
            </section>

            <!-- SEÇÃO: TEMA -->
            <section class="form-card">
                <h2 class="section-title">🎨 Personalização de Cores</h2>
                <p class="section-sub">Escolha um tema pronto ou personalize as cores:</p>

                <div class="paletas-grid">
                    <?php foreach ($paletas as $idx => $pal): ?>
                        <button type="button" 
                                class="paleta <?php echo ($tema['cor_primaria'] === $pal['primaria']) ? 'paleta-ativa' : ''; ?>"
                                onclick="aplicarPaleta('<?php echo $pal['primaria']; ?>', '<?php echo $pal['secundaria']; ?>', '<?php echo $pal['fundo']; ?>', this)">
                            <div class="paleta-cores">
                                <span style="background: <?php echo $pal['primaria']; ?>"></span>
                                <span style="background: <?php echo $pal['secundaria']; ?>"></span>
                                <span style="background: <?php echo $pal['fundo']; ?>"></span>
                            </div>
                            <span class="paleta-nome"><?php echo $pal['nome']; ?></span>
                        </button>
                    <?php endforeach; ?>
                </div>

                <div class="grid-3">
                    <div class="form-group">
                        <label>Cor primária</label>
                        <input type="color" name="cor_primaria" id="cor_primaria"
                               value="<?php echo htmlspecialchars($tema['cor_primaria'] ?? '#0f2b46'); ?>"
                               onchange="atualizarPreview()">
                    </div>
                    <div class="form-group">
                        <label>Cor secundária</label>
                        <input type="color" name="cor_secundaria" id="cor_secundaria"
                               value="<?php echo htmlspecialchars($tema['cor_secundaria'] ?? '#c9a227'); ?>"
                               onchange="atualizarPreview()">
                    </div>
                    <div class="form-group">
                        <label>Cor de fundo</label>
                        <input type="color" name="cor_fundo" id="cor_fundo"
                               value="<?php echo htmlspecialchars($tema['cor_fundo'] ?? '#f4f6f9'); ?>"
                               onchange="atualizarPreview()">
                    </div>
                </div>
            </section>

            <div class="form-actions-fixed">
                <a href="dashboard.php" class="btn-cancelar">Cancelar</a>
                <button type="submit" class="btn-salvar">💾 Salvar Alterações</button>
            </div>
        </form>
    </main>

    <script>
        function aplicarPaleta(primaria, secundaria, fundo, el) {
            document.getElementById('cor_primaria').value = primaria;
            document.getElementById('cor_secundaria').value = secundaria;
            document.getElementById('cor_fundo').value = fundo;
            
            // Atualiza visual
            document.querySelectorAll('.paleta').forEach(p => p.classList.remove('paleta-ativa'));
            el.classList.add('paleta-ativa');
        }

        function atualizarPreview() {
            // Remove destaque de paleta pronta quando o usuário muda a cor manualmente
            document.querySelectorAll('.paleta').forEach(p => p.classList.remove('paleta-ativa'));
        }
    </script>
</body>
</html>

Arquivo 8: index.php (Portfólio Público com tema dinâmico)

<?php
require_once 'config.php';

$dados = carregarDados();
$perfil = $dados['perfil'];
$tema = $dados['tema'];
$projetos = $dados['projetos'];

// Ordem: mais recentes primeiro
$projetos = array_reverse($projetos);
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Portfólio de <?php echo htmlspecialchars($perfil['nome'] ?? 'Aluno'); ?></title>
    <link rel="stylesheet" href="style.css?v=<?php echo time(); ?>">
    <style>
        :root {
            --cor-primaria: <?php echo htmlspecialchars($tema['cor_primaria'] ?? '#0f2b46'); ?>;
            --cor-secundaria: <?php echo htmlspecialchars($tema['cor_secundaria'] ?? '#c9a227'); ?>;
            --cor-fundo: <?php echo htmlspecialchars($tema['cor_fundo'] ?? '#f4f6f9'); ?>;
        }
    </style>
</head>
<body>
    <!-- HEADER / HERO (horizontal) -->
    <header class="hero">
        <div class="container">
            <div class="hero-content">

                <!-- Avatar à esquerda -->
                <div class="hero-avatar">
                    <?php if (!empty($perfil['avatar']) && file_exists(UPLOAD_AVATAR_DIR . $perfil['avatar'])): ?>
                        <img src="<?php echo UPLOAD_AVATAR_URL . htmlspecialchars($perfil['avatar']); ?>" alt="Foto de perfil">
                    <?php else: ?>
                        <span><?php echo strtoupper(substr($perfil['nome'] ?? 'A', 0, 1)); ?></span>
                    <?php endif; ?>
                </div>

                <!-- Informações no meio -->
                <div class="hero-info">
                    <h1><?php echo htmlspecialchars($perfil['nome'] ?? 'Meu Portfólio'); ?></h1>

                    <?php if (!empty($perfil['turma'])): ?>
                        <p class="hero-turma">🎓 <?php echo htmlspecialchars($perfil['turma']); ?></p>
                    <?php endif; ?>

                    <?php if (!empty($perfil['bio'])): ?>
                        <p class="hero-sub"><?php echo htmlspecialchars($perfil['bio']); ?></p>
                    <?php endif; ?>

                    <?php if (!empty($perfil['github']) || !empty($perfil['linkedin']) || !empty($perfil['email'])): ?>
                        <div class="hero-links">
                            <?php if (!empty($perfil['github'])): ?>
                                <a href="<?php echo htmlspecialchars($perfil['github']); ?>" target="_blank" rel="noopener">💻 GitHub</a>
                            <?php endif; ?>
                            <?php if (!empty($perfil['linkedin'])): ?>
                                <a href="<?php echo htmlspecialchars($perfil['linkedin']); ?>" target="_blank" rel="noopener">💼 LinkedIn</a>
                            <?php endif; ?>
                            <?php if (!empty($perfil['email'])): ?>
                                <a href="mailto:<?php echo htmlspecialchars($perfil['email']); ?>">✉️ E-mail</a>
                            <?php endif; ?>
                        </div>
                    <?php endif; ?>
                </div>

                <!-- Estatísticas à direita -->
                <div class="hero-stats">
                    <div class="hero-stat">
                        <strong><?php echo count($projetos); ?></strong>
                        <span>Projetos</span>
                    </div>
                    <div class="hero-stat">
                        <strong><?php echo date('Y'); ?></strong>
                        <span>Ano</span>
                    </div>
                </div>

            </div>
        </div>
    </header>

    <!-- PROJETOS -->
    <main class="container">
        <?php if (empty($projetos)): ?>
            <div class="empty-state">
                <span class="empty-icon">📭</span>
                <h2>Nenhum projeto publicado</h2>
                <p>Os projetos aparecerão aqui quando forem cadastrados.</p>
            </div>
        <?php else: ?>
            <h2 class="section-title">Meus Projetos</h2>
            <div class="grid-projetos">
                <?php foreach ($projetos as $p): ?>
                    <article class="card-projeto">
                        <div class="card-img">
                            <?php if (!empty($p['imagem']) && file_exists(UPLOAD_IMG_DIR . $p['imagem'])): ?>
                                <img src="<?php echo UPLOAD_IMG_URL . htmlspecialchars($p['imagem']); ?>" 
                                     alt="<?php echo htmlspecialchars($p['titulo']); ?>"
                                     loading="lazy">
                            <?php else: ?>
                                <div class="card-noimg"><span>💻</span></div>
                            <?php endif; ?>
                        </div>
                        <div class="card-body">
                            <h3><?php echo htmlspecialchars($p['titulo']); ?></h3>

                            <?php if (!empty($p['descricao'])): ?>
                                <p class="card-desc"><?php echo htmlspecialchars($p['descricao']); ?></p>
                            <?php endif; ?>

                            <?php if (!empty($p['tecnologias'])): ?>
                                <div class="card-techs">
                                    <?php foreach (explode(',', $p['tecnologias']) as $t): 
                                        $t = trim($t);
                                        if ($t === '') continue;
                                    ?>
                                        <span class="tech-tag"><?php echo htmlspecialchars($t); ?></span>
                                    <?php endforeach; ?>
                                </div>
                            <?php endif; ?>

                            <a href="<?php echo htmlspecialchars($p['link']); ?>" 
                               class="btn-acessar" 
                               target="_blank" 
                               rel="noopener">
                                🚀 Acessar Projeto
                            </a>
                        </div>
                    </article>
                <?php endforeach; ?>
            </div>
        <?php endif; ?>
    </main>

    <!-- FOOTER -->
    <footer class="footer">
        <p>Portfólio de <strong><?php echo htmlspecialchars($perfil['nome'] ?? 'Aluno'); ?></strong></p>
        <?php if (!empty($perfil['turma'])): ?>
            <p class="footer-turma"><?php echo htmlspecialchars($perfil['turma']); ?> • <?php echo date('Y'); ?></p>
        <?php endif; ?>
        <a href="login.php" class="footer-admin">🔐 Gerenciar Portfólio</a>
    </footer>
</body>
</html>

Arquivo 9: style.css

/* ==========================================================
   style.css - Portfólio Público
   Usa variáveis CSS dinâmicas:
   --cor-primaria, --cor-secundaria, --cor-fundo
   ========================================================== */

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

body {
    font-family: 'Segoe UI', 'Helvetica', sans-serif;
    background: var(--cor-fundo, #f4f6f9);
    color: #2d3748;
    min-height: 100vh;
    line-height: 1.6;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 30px;
}

/* ==========================================================
   HERO (cabeçalho do portfólio) - Horizontal
   ========================================================== */
.hero {
    background: linear-gradient(135deg, var(--cor-primaria, #0f2b46) 0%, #0a1f34 100%);
    color: white;
    padding: 30px 0;
    border-bottom: 4px solid var(--cor-secundaria, #c9a227);
    position: relative;
    overflow: hidden;
}

.hero::before {
    content: '';
    position: absolute;
    top: -50%;
    right: -10%;
    width: 400px;
    height: 400px;
    background: radial-gradient(circle, rgba(201, 162, 39, 0.12) 0%, transparent 70%);
    border-radius: 50%;
    pointer-events: none;
}

.hero-content {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 25px;
    flex-wrap: wrap;
}

/* Avatar compacto */
.hero-avatar {
    width: 90px;
    height: 90px;
    background: linear-gradient(135deg, var(--cor-secundaria, #c9a227), #b8901d);
    color: var(--cor-primaria, #0f2b46);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2.4rem;
    font-weight: 800;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
    border: 3px solid white;
    overflow: hidden;
    flex-shrink: 0;
}

.hero-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Bloco de informações (nome, turma, bio, links) */
.hero-info {
    flex: 1;
    min-width: 220px;
}

.hero-info h1 {
    font-size: 1.7rem;
    margin-bottom: 4px;
    letter-spacing: -0.3px;
    font-weight: 800;
    line-height: 1.2;
}

.hero-turma {
    color: var(--cor-secundaria, #c9a227);
    font-size: 0.88rem;
    font-weight: 600;
    letter-spacing: 0.5px;
    margin-bottom: 6px;
}

.hero-sub {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.88rem;
    margin-bottom: 10px;
    line-height: 1.5;
}

.hero-links {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.hero-links a {
    color: rgba(255, 255, 255, 0.85);
    text-decoration: none;
    padding: 5px 14px;
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 16px;
    font-size: 0.78rem;
    font-weight: 600;
    transition: all 0.2s ease;
}

.hero-links a:hover {
    background: rgba(255, 255, 255, 0.15);
    border-color: var(--cor-secundaria, #c9a227);
    color: var(--cor-secundaria, #c9a227);
}

/* Estatísticas compactas à direita */
.hero-stats {
    display: flex;
    gap: 25px;
    flex-shrink: 0;
    margin-left: auto;
    padding-left: 25px;
    border-left: 1px solid rgba(255, 255, 255, 0.15);
}

.hero-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.hero-stat strong {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--cor-secundaria, #c9a227);
    line-height: 1;
}

.hero-stat span {
    font-size: 0.68rem;
    color: rgba(255, 255, 255, 0.6);
    text-transform: uppercase;
    letter-spacing: 1.2px;
    margin-top: 4px;
}

/* ==========================================================
   PROJETOS
   ========================================================== */
main {
    padding: 40px 0;
}

.section-title {
    color: var(--cor-primaria, #0f2b46);
    font-size: 1.5rem;
    margin-bottom: 25px;
    padding-bottom: 12px;
    border-bottom: 2px solid rgba(0, 0, 0, 0.08);
    font-weight: 700;
}

.grid-projetos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 30px;
}

.card-projeto {
    background: white;
    border-radius: 14px;
    overflow: hidden;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
    transition: all 0.3s ease;
    display: flex;
    flex-direction: column;
}

.card-projeto:hover {
    transform: translateY(-8px);
    box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
}

.card-img {
    height: 200px;
    background: linear-gradient(135deg, var(--cor-primaria, #0f2b46) 0%, #0a1f34 100%);
    overflow: hidden;
    position: relative;
}

.card-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.4s ease;
    display: block;
}

.card-projeto:hover .card-img img {
    transform: scale(1.08);
}

.card-noimg {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    font-size: 4rem;
    color: rgba(255, 255, 255, 0.35);
}

.card-body {
    padding: 25px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    flex: 1;
}

.card-body h3 {
    color: var(--cor-primaria, #0f2b46);
    font-size: 1.2rem;
    font-weight: 800;
    line-height: 1.3;
}

.card-desc {
    color: #64748b;
    font-size: 0.92rem;
    line-height: 1.6;
    flex: 1;
}

.card-techs {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.tech-tag {
    background: var(--cor-fundo, #f4f6f9);
    color: #2d3748;
    padding: 4px 12px;
    border-radius: 6px;
    font-size: 0.72rem;
    font-weight: 600;
    border: 1px solid rgba(0, 0, 0, 0.08);
}

.btn-acessar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 14px 24px;
    background: linear-gradient(135deg, var(--cor-primaria, #0f2b46), #1e5b96);
    color: white;
    text-decoration: none;
    border-radius: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-size: 0.85rem;
    transition: all 0.25s ease;
    margin-top: 8px;
    border: none;
    cursor: pointer;
}

.btn-acessar:hover {
    background: linear-gradient(135deg, #0a1f34, var(--cor-primaria, #0f2b46));
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}

/* ==========================================================
   EMPTY STATE
   ========================================================== */
.empty-state {
    text-align: center;
    padding: 100px 30px;
    background: white;
    border-radius: 14px;
    border: 2px dashed rgba(0, 0, 0, 0.1);
}

.empty-icon {
    font-size: 5rem;
    display: block;
    margin-bottom: 20px;
}

.empty-state h2 {
    color: var(--cor-primaria, #0f2b46);
    margin-bottom: 10px;
    font-size: 1.4rem;
}

.empty-state p {
    color: #64748b;
}

/* ==========================================================
   FOOTER
   ========================================================== */
.footer {
    background: #0a1f34;
    color: rgba(255, 255, 255, 0.7);
    padding: 35px 20px;
    text-align: center;
    border-top: 4px solid var(--cor-secundaria, #c9a227);
    margin-top: 50px;
}

.footer p {
    font-size: 0.92rem;
    margin-bottom: 6px;
}

.footer strong {
    color: var(--cor-secundaria, #c9a227);
}

.footer-turma {
    font-size: 0.82rem !important;
    color: rgba(255, 255, 255, 0.5) !important;
    letter-spacing: 1px;
}

.footer-admin {
    display: inline-block;
    margin-top: 15px;
    color: var(--cor-secundaria, #c9a227);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 600;
    padding: 8px 18px;
    border: 1px solid var(--cor-secundaria, #c9a227);
    border-radius: 20px;
    transition: all 0.2s ease;
}

.footer-admin:hover {
    background: var(--cor-secundaria, #c9a227);
    color: var(--cor-primaria, #0f2b46);
}

/* ==========================================================
   RESPONSIVO
   ========================================================== */
@media (max-width: 900px) {
    .hero-info h1 {
        font-size: 1.5rem;
    }

    .grid-projetos {
        grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
        gap: 20px;
    }
}

@media (max-width: 700px) {
    /* Mobile: hero volta a ser vertical e centralizado */
    .hero {
        padding: 35px 0 30px;
    }

    .hero-content {
        flex-direction: column;
        text-align: center;
        gap: 15px;
    }

    .hero-avatar {
        width: 80px;
        height: 80px;
        font-size: 2rem;
    }

    .hero-info h1 {
        font-size: 1.4rem;
    }

    .hero-turma {
        font-size: 0.85rem;
    }

    .hero-sub {
        font-size: 0.85rem;
        max-width: 100%;
    }

    .hero-links {
        justify-content: center;
    }

    .hero-stats {
        margin-left: 0;
        padding-left: 0;
        border-left: none;
        padding-top: 15px;
        border-top: 1px solid rgba(255, 255, 255, 0.15);
        justify-content: center;
        width: 100%;
        gap: 40px;
    }

    .hero-stat strong {
        font-size: 1.5rem;
    }
}

@media (max-width: 600px) {
    .container {
        padding: 0 18px;
    }

    .grid-projetos {
        grid-template-columns: 1fr;
    }

    main {
        padding: 30px 0;
    }

    .section-title {
        font-size: 1.3rem;
    }

    .card-body {
        padding: 20px;
    }

    .empty-state {
        padding: 60px 20px;
    }
}

Arquivo 10: admin.css

/* admin.css - Estilo do dashboard */

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

:root {
    --navy: #0f2b46;
    --navy-soft: #1e5b96;
    --navy-dark: #0a1f34;
    --gold: #c9a227;
    --bg: #f4f6f9;
    --white: #ffffff;
    --border: #d9e0e8;
    --text: #2d3748;
    --text-soft: #64748b;
    --success: #1f9d55;
    --danger: #c53030;
}

body {
    font-family: 'Segoe UI', sans-serif;
    background: var(--bg);
    color: var(--text);
    min-height: 100vh;
    line-height: 1.6;
}

/* ==========================================================
   LOGIN
   ========================================================== */
body.login-body {
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, var(--navy) 0%, var(--navy-dark) 100%);
    padding: 20px;
}

.login-card {
    background: white;
    padding: 50px 40px;
    border-radius: 16px;
    max-width: 400px;
    width: 100%;
    text-align: center;
    box-shadow: 0 25px 60px rgba(0, 0, 0, 0.4);
    border-top: 5px solid var(--gold);
}

.login-icon { font-size: 3rem; margin-bottom: 15px; }

.login-card h1 {
    color: var(--navy);
    font-size: 1.5rem;
    margin-bottom: 6px;
}

.login-sub {
    color: var(--text-soft);
    font-size: 0.9rem;
    margin-bottom: 30px;
}

.login-back {
    display: inline-block;
    margin-top: 20px;
    color: var(--text-soft);
    text-decoration: none;
    font-size: 0.85rem;
}

.login-back:hover { color: var(--navy-soft); }

/* ==========================================================
   TOPBAR
   ========================================================== */
.topbar {
    background: var(--navy);
    color: white;
    border-bottom: 3px solid var(--gold);
    position: sticky;
    top: 0;
    z-index: 100;
}

.topbar-content {
    max-width: 1200px;
    margin: 0 auto;
    padding: 16px 30px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 15px;
}

.topbar-brand {
    display: flex;
    align-items: center;
    gap: 12px;
}

.topbar-brand span { font-size: 1.8rem; }
.topbar-brand strong { display: block; font-size: 1.05rem; }
.topbar-brand small {
    display: block;
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.75rem;
}

.topbar-nav {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.topbar-nav a {
    color: rgba(255, 255, 255, 0.85);
    text-decoration: none;
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 0.85rem;
    font-weight: 500;
    transition: all 0.2s;
    border: 1px solid rgba(255, 255, 255, 0.2);
}

.topbar-nav a:hover {
    background: rgba(255, 255, 255, 0.1);
    color: white;
}

.btn-sair {
    background: rgba(197, 48, 48, 0.3);
    border-color: #ffb3b3 !important;
    color: #ffb3b3 !important;
}

.btn-sair:hover {
    background: var(--danger) !important;
    color: white !important;
}

/* ==========================================================
   CONTAINER
   ========================================================== */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 40px 30px;
}

.container-narrow { max-width: 800px; }

/* ==========================================================
   PERFIL RESUMO (no dashboard)
   ========================================================== */
.perfil-resumo {
    background: white;
    padding: 25px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    gap: 20px;
    margin-bottom: 30px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
    border-left: 5px solid var(--gold);
}

.perfil-avatar {
    width: 70px;
    height: 70px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--navy), var(--navy-soft));
    color: var(--gold);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    font-weight: 800;
    overflow: hidden;
    flex-shrink: 0;
}

.perfil-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.perfil-info { flex: 1; }
.perfil-info h2 { color: var(--navy); font-size: 1.3rem; margin-bottom: 4px; }
.perfil-info p { color: var(--text-soft); font-size: 0.9rem; }

.btn-perfil {
    padding: 10px 20px;
    background: var(--bg);
    color: var(--navy);
    text-decoration: none;
    border-radius: 8px;
    font-size: 0.85rem;
    font-weight: 700;
    border: 2px solid var(--border);
    transition: all 0.2s;
}

.btn-perfil:hover {
    background: var(--navy);
    color: white;
    border-color: var(--navy);
}

/* ==========================================================
   LISTA HEADER
   ========================================================== */
.lista-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 25px;
    flex-wrap: wrap;
    gap: 15px;
}

.lista-header h1 { color: var(--navy); font-size: 1.6rem; margin-bottom: 4px; }
.lista-sub { color: var(--text-soft); font-size: 0.9rem; }

.btn-novo {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 24px;
    background: var(--navy);
    color: white;
    text-decoration: none;
    border-radius: 8px;
    font-weight: 700;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    transition: all 0.2s;
}

.btn-novo:hover {
    background: var(--navy-dark);
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(15, 43, 70, 0.25);
}

/* ==========================================================
   GRID ADMIN
   ========================================================== */
.grid-projetos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 20px;
}

.card-admin {
    background: white;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
    transition: all 0.25s;
    display: flex;
    flex-direction: column;
}

.card-admin:hover {
    transform: translateY(-4px);
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.12);
}

.card-img {
    height: 160px;
    background: linear-gradient(135deg, var(--navy), var(--navy-dark));
    overflow: hidden;
}

.card-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.card-noimg {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    font-size: 3rem;
    color: rgba(255, 255, 255, 0.35);
}

.card-body {
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    flex: 1;
}

.card-body h3 {
    color: var(--navy);
    font-size: 1.05rem;
    line-height: 1.3;
}

.card-desc {
    color: var(--text-soft);
    font-size: 0.85rem;
    line-height: 1.5;
    flex: 1;
}

.card-actions {
    display: flex;
    gap: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

.btn-mini {
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    text-decoration: none;
    font-size: 1rem;
    transition: all 0.2s;
}

.btn-edit { background: #eef4fb; color: var(--navy-soft); }
.btn-edit:hover { background: var(--navy-soft); color: white; }
.btn-del { background: #fdecea; color: var(--danger); }
.btn-del:hover { background: var(--danger); color: white; }

/* ==========================================================
   FORMULÁRIOS
   ========================================================== */
.form-card {
    background: white;
    padding: 30px;
    border-radius: 12px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
    margin-bottom: 25px;
}

.section-title {
    color: var(--navy);
    font-size: 1.2rem;
    margin-bottom: 20px;
    padding-bottom: 12px;
    border-bottom: 2px solid var(--border);
}

.section-sub {
    color: var(--text-soft);
    font-size: 0.9rem;
    margin-bottom: 20px;
}

.form-group {
    display: flex;
    flex-direction: column;
    margin-bottom: 20px;
}

.form-group label {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--navy);
    margin-bottom: 6px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.form-group input,
.form-group textarea {
    padding: 12px 16px;
    border: 2px solid var(--border);
    border-radius: 8px;
    font-size: 0.95rem;
    font-family: inherit;
    transition: all 0.2s;
    background: white;
    color: var(--text);
    width: 100%;
}

.form-group input:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--navy-soft);
    box-shadow: 0 0 0 3px rgba(30, 91, 150, 0.1);
}

.form-group input[type="color"] {
    height: 50px;
    padding: 4px;
    cursor: pointer;
}

.form-group small {
    margin-top: 6px;
    color: var(--text-soft);
    font-size: 0.78rem;
    font-style: italic;
}

.form-group textarea {
    resize: vertical;
    min-height: 100px;
}

.grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
}

.grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
}

/* Upload do perfil */
.perfil-upload {
    display: flex;
    align-items: center;
    gap: 25px;
    padding: 20px;
    background: var(--bg);
    border-radius: 12px;
    margin-bottom: 25px;
}

.perfil-avatar-grande {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--navy), var(--navy-soft));
    color: var(--gold);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2.8rem;
    font-weight: 800;
    overflow: hidden;
    flex-shrink: 0;
    border: 4px solid white;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.perfil-avatar-grande img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.perfil-upload-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.perfil-upload-info label {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--navy);
    text-transform: uppercase;
}

.perfil-upload-info small {
    color: var(--text-soft);
    font-size: 0.78rem;
}

/* Preview da imagem */
.preview-img {
    max-width: 200px;
    border-radius: 8px;
    border: 2px solid var(--border);
    margin: 10px 0;
}

/* Paletas de cores */
.paletas-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 12px;
    margin-bottom: 25px;
}

.paleta {
    background: white;
    border: 2px solid var(--border);
    border-radius: 10px;
    padding: 12px;
    cursor: pointer;
    transition: all 0.2s;
    font-family: inherit;
    text-align: left;
}

.paleta:hover {
    border-color: var(--navy-soft);
    transform: translateY(-2px);
}

.paleta-ativa {
    border-color: var(--navy);
    box-shadow: 0 0 0 2px var(--navy);
}

.paleta-cores {
    display: flex;
    gap: 4px;
    margin-bottom: 8px;
}

.paleta-cores span {
    flex: 1;
    height: 30px;
    border-radius: 6px;
    border: 1px solid rgba(0,0,0,0.05);
}

.paleta-nome {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--navy);
    display: block;
}

/* Ações do formulário */
.form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    padding-top: 20px;
    margin-top: 20px;
    border-top: 1px solid var(--border);
}

.form-actions-fixed {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    padding: 20px 0;
    position: sticky;
    bottom: 0;
    background: var(--bg);
}

.btn-cancelar,
.btn-salvar {
    padding: 13px 28px;
    border-radius: 8px;
    font-weight: 700;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    text-decoration: none;
    cursor: pointer;
    font-family: inherit;
    border: none;
    transition: all 0.2s;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.btn-cancelar {
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
}

.btn-cancelar:hover { background: #e6ecf3; }

.btn-salvar {
    background: var(--navy);
    color: white;
}

.btn-salvar:hover {
    background: var(--navy-dark);
    transform: translateY(-1px);
    box-shadow: 0 6px 15px rgba(15, 43, 70, 0.2);
}

.btn-full {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 14px;
    background: var(--navy);
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 0.95rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    cursor: pointer;
    font-family: inherit;
    transition: all 0.2s;
    margin-top: 10px;
}

.btn-full:hover {
    background: var(--navy-dark);
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(15, 43, 70, 0.3);
}

/* ==========================================================
   ALERTAS
   ========================================================== */
.alert {
    padding: 14px 20px;
    border-radius: 8px;
    margin-bottom: 20px;
    border-left: 5px solid;
    font-size: 0.9rem;
    line-height: 1.6;
}

.alert-success {
    background: #e6f4ea;
    border-color: var(--success);
    color: #1e4620;
}

.alert-error {
    background: #fdecea;
    border-color: var(--danger);
    color: #5c1a1a;
}

/* ==========================================================
   EMPTY
   ========================================================== */
.empty-state {
    text-align: center;
    padding: 70px 30px;
    background: white;
    border-radius: 12px;
    border: 2px dashed var(--border);
}

.empty-icon { font-size: 4rem; display: block; margin-bottom: 15px; }
.empty-state h2 { color: var(--navy); margin-bottom: 8px; }
.empty-state p { color: var(--text-soft); margin-bottom: 20px; }

/* ==========================================================
   RESPONSIVO
   ========================================================== */
@media (max-width: 700px) {
    .container { padding: 25px 18px; }
    .topbar-content { flex-direction: column; align-items: stretch; }
    .topbar-nav { justify-content: center; }
    .topbar-nav a { flex: 1; text-align: center; }
    .lista-header { flex-direction: column; align-items: stretch; }
    .btn-novo { justify-content: center; }
    .grid-projetos { grid-template-columns: 1fr; }
    .perfil-resumo { flex-direction: column; text-align: center; }
    .grid-2, .grid-3 { grid-template-columns: 1fr; }
    .paletas-grid { grid-template-columns: 1fr 1fr; }
    .form-card { padding: 20px; }
    .form-actions { flex-direction: column-reverse; }
    .btn-cancelar, .btn-salvar { width: 100%; justify-content: center; }
    .perfil-upload { flex-direction: column; text-align: center; }
}