Compare commits

..

1 Commits

Author SHA1 Message Date
17060c054c docs: classify Amare MVP Linear issues vs Gitea main (MAN-135)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 13:40:09 -03:00
34 changed files with 204 additions and 496 deletions

View File

@@ -4,10 +4,7 @@ declare(strict_types=1);
namespace App\Application\Queries\Marketing;
use App\Domain\Marketing\PortfolioVertical;
use App\Models\PortfolioCase;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
final class GetPublishedPortfolioCases
@@ -15,37 +12,12 @@ final class GetPublishedPortfolioCases
/**
* @return Collection<int, PortfolioCase>
*/
public function __invoke(?PortfolioVertical $vertical = null): Collection
public function __invoke(): Collection
{
return $this->baseQuery($vertical)->get();
}
/**
* @return LengthAwarePaginator<int, PortfolioCase>
*/
public function paginate(?PortfolioVertical $vertical = null, int $perPage = 9): LengthAwarePaginator
{
return $this->baseQuery($vertical)->paginate($perPage)->withQueryString();
}
/**
* @return Builder<PortfolioCase>
*/
private function baseQuery(?PortfolioVertical $vertical): Builder
{
$query = PortfolioCase::query()
return PortfolioCase::query()
->published()
->with(['images'])
->orderBy('sort_order');
if ($vertical !== null) {
$query->where(function (Builder $builder) use ($vertical): void {
foreach ($vertical->eventTypePatterns() as $pattern) {
$builder->orWhere('event_type', 'ilike', $pattern);
}
});
}
return $query;
->orderBy('sort_order')
->get();
}
}

View File

@@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Domain\Marketing;
/**
* Public portfolio verticals used to separate Casamentos and Corporate proof.
*
* Matching is intentionally tolerant of free-text `event_type` values already
* stored in the CMS (e.g. "Casamento", "Mini wedding", "Corporativo").
*/
enum PortfolioVertical: string
{
case Casamentos = 'casamentos';
case Corporate = 'corporate';
public function label(): string
{
return match ($this) {
self::Casamentos => 'Casamentos',
self::Corporate => 'Corporate',
};
}
/**
* @return list<string>
*/
public function eventTypePatterns(): array
{
return match ($this) {
self::Casamentos => ['%casamento%', '%wedding%', '%social%'],
self::Corporate => ['%corporat%', '%empresa%', '%business%'],
};
}
public static function tryFromQuery(?string $value): ?self
{
if ($value === null || $value === '') {
return null;
}
return self::tryFrom($value);
}
}

View File

@@ -6,35 +6,33 @@ namespace App\Http\Controllers\PublicSite;
use App\Application\Data\PageMeta;
use App\Application\Queries\Marketing\FindPublishedPortfolioCaseBySlug;
use App\Application\Queries\Marketing\GetPublishedPortfolioCases;
use App\Domain\Marketing\PortfolioVertical;
use App\Http\Controllers\Controller;
use App\Models\PortfolioCase;
use App\Models\SiteSetting;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Pagination\LengthAwarePaginator;
final class PortfolioController extends Controller
{
public function index(Request $request, GetPublishedPortfolioCases $getPublishedPortfolioCases): View
public function index(): View
{
$settings = SiteSetting::instance();
$vertical = PortfolioVertical::tryFromQuery($request->query('vertente'));
$cases = $getPublishedPortfolioCases->paginate($vertical);
/** @var LengthAwarePaginator<int, PortfolioCase> $cases */
$cases = PortfolioCase::query()
->published()
->with(['images'])
->orderBy('sort_order')
->paginate(9);
return view('pages.portfolio.index', [
'cases' => $cases,
'vertical' => $vertical,
'siteSettings' => $settings,
'pageMeta' => PageMeta::forPage(
canonical: route('portfolio.index', array_filter([
'vertente' => $vertical?->value,
])),
canonical: route('portfolio.index'),
settings: $settings,
title: PageMeta::withBrandSuffix(
$vertical === null ? 'Portfólio' : 'Portfólio — '.$vertical->label(),
$settings,
),
title: PageMeta::withBrandSuffix('Portfólio', $settings),
description: 'Casos reais de eventos conduzidos pela '.$settings->brand_name.'.',
),
]);

View File

@@ -0,0 +1,118 @@
# MAN-135 — Classificar issues Amare MVP vs Gitea `main`
**Issue:** [Research: classificar issues Amare MVP vs Gitea main](https://linear.app/maneco-workspace/issue/MAN-135/research-classificar-issues-amare-mvp-vs-gitea-main)
**Mapa:** [MAN-134](https://linear.app/maneco-workspace/issue/MAN-134/mapa-classificar-amare-mvp-linear-gitea)
**Branch:** `research/man-135-classify-amare-mvp`
**HEAD canônico inventariado:** `origin/main` @ `93b680c` (`docs: require Linear issues and auto-ship PRs (#4)`)
**Data:** 2026-08-13
## Regras (de MAN-134)
| Regra | Aplicação |
| --- | --- |
| Code truth | Gitea `origin/main` (`manoel-freitas/amare`) |
| Done = shipped | commit presente em `origin/main` |
| GitHub | só pista (`amore-site`); não canônico |
| Pós-MVP | fora de escopo: MAN-111, 112, 113, 114, 115, 131 |
| Status Linear | **não** alterados neste research (exceto fechar MAN-135) |
## Fontes
| Fonte | Uso |
| --- | --- |
| Linear MCP `list_issues` / `get_issue` project **Amare — Lançamento MVP** | universo, status, labels, ACs |
| `git fetch origin` + `git log origin/main` | evidência shipped |
| `tea pr list --state all` (0 open; #1#4 merged) | PRs Gitea |
| `gh pr list` / `gh pr view` (GitHub mirror) | pistas #63, #64 |
| Branches `origin/*` | trabalho em voo |
| Cross-check | [MAN-136 orphans](https://linear.app/maneco-workspace/issue/MAN-136/research-inventariar-merges-orfaos-no-gitea), [MAN-137 MAN-126 state](https://linear.app/maneco-workspace/issue/MAN-137/research-estado-real-de-casamentos-corporate-man-126) (branches locais; **não** em `main` ainda) |
## Contagens (36 issues in-scope)
| Bucket | Count |
| --- | ---: |
| **shipped** | 14 |
| **em voo** | 5 |
| **não começou** | 12 |
| · cliente | 5 |
| · engenharia | 5 |
| · ops-lançamento | 2 |
| **Linear podre** | 5 |
| **Total classificado** | **36** |
| Excluídos Pós-MVP | 6 |
| Issues no projeto | 42 |
---
## Tabela canônica
| Issue | Linear status | Bucket | Evidência / nota |
| --- | --- | --- | --- |
| [Implementar Home e páginas institucionais do MVP](https://linear.app/maneco-workspace/issue/MAN-102/implementar-home-e-paginas-institucionais-do-mvp) | Done | **shipped** | Site público + home editorial em `main` (ex. `#53`, heros, `/sobre`, `/servicos`) |
| [Implementar portfólio e depoimentos](https://linear.app/maneco-workspace/issue/MAN-103/implementar-portfolio-e-depoimentos) | Done | **shipped** | Portfólio/depoimentos + seed fix `49fbc0f` (MAN-103) |
| [Implementar formulário de solicitação de proposta](https://linear.app/maneco-workspace/issue/MAN-104/implementar-formulario-de-solicitacao-de-proposta) | Done | **shipped** | Briefing/contato em `main` (ex. `eed8240`) |
| [Implementar tracking simples da origem dos leads](https://linear.app/maneco-workspace/issue/MAN-105/implementar-tracking-simples-da-origem-dos-leads) | Done | **shipped** | `37296f7` (MAN-105) / GH #37 |
| [Definir e implementar atualização simples de conteúdo](https://linear.app/maneco-workspace/issue/MAN-106/definir-e-implementar-atualizacao-simples-de-conteudo) | Done | **shipped** | `4001cd0` (MAN-106 AC4) / GH #33 |
| [Finalizar SEO técnico e metadados sociais](https://linear.app/maneco-workspace/issue/MAN-107/finalizar-seo-tecnico-e-metadados-sociais) | Done | **shipped** | SEO desde ship público (`3dc1f44` et al.) |
| [Rodar auditoria Lighthouse e corrigir performance do site público](https://linear.app/maneco-workspace/issue/MAN-109/rodar-auditoria-lighthouse-e-corrigir-performance-do-site-publico) | Done | **shipped** | `0ecae4c` (MAN-109) / GH #43 |
| [Auditar se Vue + Livewire faz sentido na arquitetura atual](https://linear.app/maneco-workspace/issue/MAN-117/auditar-se-vue-livewire-faz-sentido-na-arquitetura-atual) | Done | **shipped** | `2e28603` reconcilia stack declarada |
| [Portar identidade visual do site público para o admin em Filament](https://linear.app/maneco-workspace/issue/MAN-118/portar-identidade-visual-do-site-publico-para-o-admin-em-filament) | Done | **shipped** | `63c0269` (MAN-118) / GH #40 |
| [Estabilizar baselines de regressão visual no CI (paridade local×CI)](https://linear.app/maneco-workspace/issue/MAN-119/estabilizar-baselines-de-regressao-visual-no-ci-paridade-localci) | Done | **shipped** | Trabalho de baselines chegou em `main`; remoção posterior `#44` é resolução intencional, não “Done sem merge” |
| [Fechar portões de qualidade e deploy da Fase 0 antes do lançamento](https://linear.app/maneco-workspace/issue/MAN-120/fechar-portoes-de-qualidade-e-deploy-da-fase-0-antes-do-lancamento) | Done | **shipped** | `7e68c0e` (MAN-120) / GH #39 |
| [Reencodar imagens de marca e regerar baselines visuais](https://linear.app/maneco-workspace/issue/MAN-122/reencodar-imagens-de-marca-e-regerar-baselines-visuais) | Done | **shipped** | Era de baselines/imagem em `main` (série #24/#7 + follow-ups) |
| [Deploy de staging falha no Dokploy desde 08/08 — causa server-side, sem log acessível](https://linear.app/maneco-workspace/issue/MAN-123/deploy-de-staging-falha-no-dokploy-desde-0808-causa-server-side-sem) | Done | **shipped** | Pipeline/fixes Dokploy+staging em `main` (ex. `#6``#9`, `ac08b5f`, `26a68e1`) |
| [Reconciliar documentação e changes OpenSpec após os PRs #41#49](https://linear.app/maneco-workspace/issue/MAN-124/reconciliar-documentacao-e-changes-openspec-apos-os-prs-41-49) | Done | **shipped** | `213d684` (MAN-124) / GH #50 |
| [Reestruturar jornada pública em Amare Casamentos e Amare Corporate](https://linear.app/maneco-workspace/issue/MAN-126/reestruturar-jornada-publica-em-amare-casamentos-e-amare-corporate) | In Progress | **em voo** | `origin/feat/man-126-casamentos-corporate` (1 commit ahead, não em `main`); GH PR [#63](https://github.com/manoel-freitas/amore-site/pull/63) open; **0** PR Gitea. Ver MAN-137 |
| [Executar QA de responsividade e acessibilidade](https://linear.app/maneco-workspace/issue/MAN-108/executar-qa-de-responsividade-e-acessibilidade) | In Progress | **em voo** | Status coerente com QA aberto; PRs de suporte (#44#49) já em `main`, ACs manuais não fechados; sem PR Gitea aberto só desta issue |
| [Configurar domínio, ambiente de produção e checklist de lançamento](https://linear.app/maneco-workspace/issue/MAN-110/configurar-dominio-ambiente-de-producao-e-checklist-de-lancamento) | In Progress | **em voo** | Ops de lançamento em curso (staging/deploy em `main`; produção/DNS/checklist incompletos) |
| [Ajustar CTAs consultivos no WhatsApp e briefing comercial](https://linear.app/maneco-workspace/issue/MAN-127/ajustar-ctas-consultivos-no-whatsapp-e-briefing-comercial) | Todo | **em voo** | Fluxo 1 parcial em `main` via Gitea [#2](https://git.hellomanoel.com/manoel-freitas/amare/pulls/2) (`e2af1f6`); Fluxo 2 (indecisos) + fechamento de ACs ainda abertos; Linear Todo atrasa vs código parcial |
| [Research: classificar issues Amare MVP vs Gitea main](https://linear.app/maneco-workspace/issue/MAN-135/research-classificar-issues-amare-mvp-vs-gitea-main) | Backlog→Done* | **em voo** | Este arquivo / branch `research/man-135-classify-amare-mvp`**ainda não** em `origin/main` (*Done só no Linear após resolução; ver podre se Done sem merge) |
| [Exigir issue Linear em todo trabalho e PR](https://linear.app/maneco-workspace/issue/MAN-132/exigir-issue-linear-em-todo-trabalho-e-pr) | In Progress | **Linear podre** | Conteúdo em `main` via Gitea [#4](https://git.hellomanoel.com/manoel-freitas/amare/pulls/4) (`93b680c`); Linear ainda In Progress. GH [#64](https://github.com/manoel-freitas/amore-site/pull/64) open = mirror stale |
| [Adicionar PR template no Gitea e no GitHub](https://linear.app/maneco-workspace/issue/MAN-133/adicionar-pr-template-no-gitea-e-no-github) | In Progress | **Linear podre** | `.gitea/PULL_REQUEST_TEMPLATE.md` + `.github/pull_request_template.md` em `origin/main` (mesmo PR #4); Linear In Progress |
| [Fechar arquitetura e copy final do MVP](https://linear.app/maneco-workspace/issue/MAN-97/fechar-arquitetura-e-copy-final-do-mvp) | In Progress | **Linear podre** | In Progress sem branch/PR abertos; arquitetura/home editorial já em `main` (#46#53, #52…). Residual de copy/conteúdo vive em tickets cliente (MAN-98…) e MAN-126 |
| [Research: inventariar merges órfãos no Gitea](https://linear.app/maneco-workspace/issue/MAN-136/research-inventariar-merges-orfaos-no-gitea) | Done | **Linear podre** | Done no Linear; deliverable em `origin/research/man-136-orphan-gitea-merges`**não** merged em `main` |
| [Research: estado real de Casamentos × Corporate (MAN-126)](https://linear.app/maneco-workspace/issue/MAN-137/research-estado-real-de-casamentos-corporate-man-126) | Done | **Linear podre** | Done no Linear; `docs/research/man-137-man-126-state.md` em branch local ahead — **não** em `origin/main` |
| [Receber e consolidar conteúdo institucional e serviços da Michelle](https://linear.app/maneco-workspace/issue/MAN-98/receber-e-consolidar-conteudo-institucional-e-servicos-da-michelle) | Todo | **não começou · cliente** | Labels Conteúdo/Cliente; sem branch/PR |
| [Fechar portfólio e banco de imagens do lançamento](https://linear.app/maneco-workspace/issue/MAN-99/fechar-portfolio-e-banco-de-imagens-do-lancamento) | Todo | **não começou · cliente** | Labels Conteúdo/Cliente; sem branch/PR |
| [Consolidar depoimentos reais para o MVP](https://linear.app/maneco-workspace/issue/MAN-100/consolidar-depoimentos-reais-para-o-mvp) | Todo | **não começou · cliente** | Labels Conteúdo/Cliente; sem branch/PR |
| [Confirmar WhatsApp Business e canais de contato](https://linear.app/maneco-workspace/issue/MAN-101/confirmar-whatsapp-business-e-canais-de-contato) | Todo | **não começou · cliente** | Labels Conteúdo/Cliente; sem branch/PR |
| [Aplicar identidade visual oficial e rodada de revisão da cliente](https://linear.app/maneco-workspace/issue/MAN-128/aplicar-identidade-visual-oficial-e-rodada-de-revisao-da-cliente) | Todo | **não começou · cliente** | Labels Cliente/Design; sem branch/PR |
| [Papel Assistant não tem nenhuma permissão funcional](https://linear.app/maneco-workspace/issue/MAN-121/papel-assistant-nao-tem-nenhuma-permissao-funcional) | Backlog | **não começou · engenharia** | Enum `Assistant` existe; permissões funcionais não entregues; sem branch |
| [Provar falha dos gates e rollback antes de arquivar complete-foundation-parity](https://linear.app/maneco-workspace/issue/MAN-125/provar-falha-dos-gates-e-rollback-antes-de-arquivar-complete) | Todo | **não começou · engenharia** | ACs de prova controlada abertos; experiência **não** deve mergear em `main` |
| [Revisar privacidade, LGPD e consentimentos do MVP](https://linear.app/maneco-workspace/issue/MAN-116/revisar-privacidade-lgpd-e-consentimentos-do-mvp) | Todo | **não começou · engenharia** | Página privacidade existe; revisão/consentimentos ACs abertos; sem branch |
| [Mapa: classificar Amare MVP (Linear × Gitea)](https://linear.app/maneco-workspace/issue/MAN-134/mapa-classificar-amare-mvp-linear-gitea) | Todo | **não começou · engenharia** | Wayfinder map — inventário ainda em research filhos |
| [Grilling: lista de ações de sync após inventário](https://linear.app/maneco-workspace/issue/MAN-138/grilling-lista-de-acoes-de-sync-apos-inventario) | Backlog | **não começou · engenharia** | Depende do inventário (este + MAN-136/137) |
| [Configurar Search Console, Meta Pixel e mensuração de lançamento](https://linear.app/maneco-workspace/issue/MAN-129/configurar-search-console-meta-pixel-e-mensuracao-de-lancamento) | Todo | **não começou · ops-lançamento** | Mensuração de go-live; sem branch |
| [Preparar acesso e treinamento do painel administrativo](https://linear.app/maneco-workspace/issue/MAN-130/preparar-acesso-e-treinamento-do-painel-administrativo) | Todo | **não começou · ops-lançamento** | Acesso/treino go-live (label Cliente+Engenharia); sem branch |
### Fora de escopo (Pós-MVP)
| Issue | Nota |
| --- | --- |
| [MAN-111](https://linear.app/maneco-workspace/issue/MAN-111/pos-mvp-produzir-pdf-comercial-estatico-de-servicos) … [MAN-115](https://linear.app/maneco-workspace/issue/MAN-115/pos-mvp-mapear-convivencia-com-o-assessoria-vip), [MAN-131](https://linear.app/maneco-workspace/issue/MAN-131/pos-mvp-formulario-para-fornecedores-e-parcerias) | Não classificados (mapa Out of scope) |
---
## Hints verificados
| Hint | Resultado |
| --- | --- |
| MAN-132 + MAN-133 via Gitea PR #4, Linear In Progress | **Confirmado → Linear podre** (código shipped; status podre). Também GH #64 ainda open |
| MAN-126: GH #63 + branch, sem PR Gitea | **Confirmado → em voo** |
| Gitea 0 open PRs; #1#4 merged | **Confirmado** (`tea pr list`) |
## Ações de sync sugeridas (não executadas aqui)
1. Marcar **Done** no Linear: MAN-132, MAN-133 (e fechar GH #64 stale).
2. Reavaliar MAN-97 → Done ou decompor residual em tickets cliente/MAN-126.
3. Abrir **PR Gitea** para `feat/man-126-casamentos-corporate`; fechar ou ignorar GH #63 após mirror.
4. MAN-127: mover para In Progress; fechar Fluxo 2 / ACs restantes ou fatiar ticket.
5. Mergear (ou aceitar exceção) branches de research MAN-136/137/135 se “Done” exigir `main`.
6. Lista priorizada fica para [MAN-138](https://linear.app/maneco-workspace/issue/MAN-138/grilling-lista-de-acoes-de-sync-apos-inventario).
## Ambiguidades
- **Wayfinder research Done:** mapa diz Done = `main`; prática atual marca Done após comentário Linear com arquivo só na branch → classificado **Linear podre** para MAN-136/137.
- **MAN-119/122:** remoção de regressão visual (`#44`) após estabilizar — tratados como shipped (resolução chegou em `main`).
- **MAN-127:** bucket **em voo** (parcial shipped + resto aberto); alternativa seria “não começou” só do residual — preferimos um bucket por issue.
- **MAN-108/110:** sem branch dedicada; “em voo” = processo In Progress, não PR aberta.

11
package-lock.json generated
View File

@@ -5,7 +5,6 @@
"packages": {
"": {
"devDependencies": {
"@fontsource/eb-garamond": "^5.2.7",
"@tailwindcss/vite": "^4.0.0",
"concurrently": "^9.0.1",
"daisyui": "^5.7.16",
@@ -50,16 +49,6 @@
"tslib": "^2.4.0"
}
},
"node_modules/@fontsource/eb-garamond": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@fontsource/eb-garamond/-/eb-garamond-5.3.0.tgz",
"integrity": "sha512-VplwHpB8FLuzl+4G5LDci1b3Z4SghiuXH8XEgaxjnOb5aue5mEt1njmDKeO6iyLeGa73LffU7PC7hzncLRx0ZA==",
"dev": true,
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",

View File

@@ -8,7 +8,6 @@
"prepare": "husky"
},
"devDependencies": {
"@fontsource/eb-garamond": "^5.2.7",
"@tailwindcss/vite": "^4.0.0",
"concurrently": "^9.0.1",
"daisyui": "^5.7.16",

View File

@@ -134,8 +134,7 @@
overflow-wrap: break-word;
}
[id$='-heading'],
.home-chapter[id] {
[id$='-heading'] {
scroll-margin-top: 5.5rem;
}
@@ -189,18 +188,6 @@
overflow-x: clip;
}
.link-editorial {
background-image: linear-gradient(currentColor, currentColor);
background-position: 0 100%;
background-repeat: no-repeat;
background-size: 100% 1px;
padding-bottom: 1px;
}
.nav-link-editorial {
position: relative;
}
@media (prefers-reduced-motion: no-preference) {
:where(a, button, input, textarea, select) {
transition-duration: var(--amare-motion-feedback);
@@ -212,45 +199,6 @@
transform: translateY(1px);
}
:where(button[aria-busy='true'], button:disabled):active {
transform: none;
}
.field-editorial {
transition:
border-color var(--amare-motion-feedback) var(--amare-ease-arrival),
background-color var(--amare-motion-feedback) var(--amare-ease-arrival);
}
.field-editorial:focus,
.field-editorial:focus-visible {
background-color: var(--amare-color-bg-deep);
}
.link-editorial {
transition: color var(--amare-motion-feedback) var(--amare-ease-arrival);
}
@media (min-width: 768px) {
.nav-link-editorial::after {
background-color: var(--amare-color-accent);
bottom: 0.55rem;
content: '';
height: 1px;
left: 0.75rem;
position: absolute;
right: 0.75rem;
transform: scaleX(0);
transform-origin: left center;
transition: transform var(--amare-motion-feedback) var(--amare-ease-arrival);
}
.nav-link-editorial:hover::after,
.nav-link-editorial:focus-visible::after {
transform: scaleX(1);
}
}
/* Transform/clip only — never fade text opacity (axe + WCAG mid-transition). */
html[data-motion="enhance"] [data-motion="page-open"]:not(.is-active) [data-motion-beat] {
transform: translateY(var(--amare-motion-distance-y));

View File

@@ -24,7 +24,7 @@
<div class="lg:col-span-6 lg:col-start-7">
<p class="max-w-xl text-amare-text-muted">Uma composição de eventos sociais e corporativos para apresentar a versatilidade da Amare.</p>
<a href="{{ route('portfolio.index') }}" class="btn btn-ghost mt-5 min-h-0 px-0 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent hover:bg-transparent hover:text-amare-accent-deep">
<span class="link-editorial">Ver portfólio completo</span>
<span class="border-b border-amare-accent pb-1">Ver portfólio completo</span>
</a>
</div>
</div>

View File

@@ -50,7 +50,7 @@
autocomplete="name"
maxlength="120"
placeholder="Seu nome"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('nome') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('nome') input-error @enderror"
@error('nome') aria-invalid="true" aria-describedby="briefing-nome-error" @enderror
>
@error('nome')
@@ -69,7 +69,7 @@
autocomplete="email"
maxlength="254"
placeholder="voce@email.com"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('email') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('email') input-error @enderror"
@error('email') aria-invalid="true" aria-describedby="briefing-email-error" @enderror
>
@error('email')
@@ -88,7 +88,7 @@
autocomplete="tel"
maxlength="40"
placeholder="(11) 90000-0000"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('telefone') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('telefone') input-error @enderror"
@error('telefone') aria-invalid="true" aria-describedby="briefing-telefone-error" @enderror
>
@error('telefone')
@@ -102,7 +102,7 @@
id="briefing-tipo_evento"
name="tipo_evento"
required
class="select field-editorial w-full text-amare-text @error('tipo_evento') select-error @enderror"
class="select w-full text-amare-text @error('tipo_evento') select-error @enderror"
@error('tipo_evento') aria-invalid="true" aria-describedby="briefing-tipo_evento-error" @enderror
>
<option value="" selected disabled>Selecione...</option>
@@ -125,7 +125,7 @@
value="{{ old('data_periodo') }}"
maxlength="80"
placeholder="Ex.: novembro de 2027"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('data_periodo') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('data_periodo') input-error @enderror"
@error('data_periodo') aria-invalid="true" aria-describedby="briefing-data_periodo-error" @enderror
>
@error('data_periodo')
@@ -143,7 +143,7 @@
required
maxlength="80"
placeholder="Ex.: São Paulo"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('cidade') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('cidade') input-error @enderror"
@error('cidade') aria-invalid="true" aria-describedby="briefing-cidade-error" @enderror
>
@error('cidade')
@@ -163,7 +163,7 @@
inputmode="numeric"
autocomplete="off"
placeholder="Ex.: 120"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('convidados') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('convidados') input-error @enderror"
@error('convidados') aria-invalid="true" aria-describedby="briefing-convidados-error" @enderror
>
@error('convidados')
@@ -180,7 +180,7 @@
value="{{ old('servico_interesse') }}"
maxlength="120"
placeholder="Ex.: planejamento completo"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('servico_interesse') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('servico_interesse') input-error @enderror"
@error('servico_interesse') aria-invalid="true" aria-describedby="briefing-servico_interesse-error" @enderror
>
@error('servico_interesse')
@@ -198,7 +198,7 @@
rows="5"
maxlength="3000"
placeholder="O que já está definido? Em que ponto você precisa de ajuda?"
class="textarea field-editorial w-full min-h-[110px] text-amare-text placeholder:text-amare-muted/60 @error('mensagem') textarea-error @enderror"
class="textarea w-full min-h-[110px] text-amare-text placeholder:text-amare-muted/60 @error('mensagem') textarea-error @enderror"
@error('mensagem') aria-invalid="true" aria-describedby="briefing-mensagem-error" @enderror
>{{ old('mensagem') }}</textarea>
@error('mensagem')

View File

@@ -19,7 +19,7 @@
@if (filled($settings->hero_secondary_cta_label))
<a href="{{ route('portfolio.index') }}" class="btn btn-ghost min-h-0 px-0 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent hover:bg-transparent hover:text-amare-accent-deep">
<span class="link-editorial">{{ $settings->hero_secondary_cta_label }}</span>
<span class="border-b border-amare-accent pb-1">{{ $settings->hero_secondary_cta_label }}</span>
</a>
@endif
</div>

View File

@@ -64,7 +64,7 @@
href="{{ route('packages.show', $package->slug) }}"
class="btn btn-ghost min-h-0 px-0 text-xs font-semibold uppercase tracking-[0.09em] text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"
>
<span class="link-editorial">Conhecer esta modalidade</span>
<span class="border-b border-amare-accent pb-1">Conhecer esta modalidade</span>
</a>
</div>
</article>

View File

@@ -35,7 +35,7 @@
</ul>
<p>
<a href="{{ route('about') }}" class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep">
<span class="link-editorial">Conhecer a Amare</span>
<span class="border-b border-amare-accent pb-1">Conhecer a Amare</span>
</a>
</p>
</div>

View File

@@ -27,7 +27,7 @@
<p>
<a href="{{ route('services.index') }}" class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep">
<span class="link-editorial">Ver todos os serviços</span>
<span class="border-b border-amare-accent pb-1">Ver todos os serviços</span>
</a>
</p>
</div>

View File

@@ -9,7 +9,7 @@
<ol class="grid gap-8 border-t border-amare-border pt-6 md:grid-cols-3">
@foreach ($packages as $package)
@php($contactLink = \App\Support\PackageContactLink::for($settings, $package->name))
<li class="space-y-5 border-t border-amare-border pt-4" data-reveal data-reveal-from="up"><p class="text-xs font-semibold uppercase tracking-[.14em] text-amare-accent">{{ $package->level }}</p><h3 class="text-3xl font-medium">{{ $package->name }}</h3><p class="text-amare-muted">{{ $package->summary }}</p><ul class="space-y-2 text-sm text-amare-text-muted">@foreach ($package->scope_items as $item)<li class="border-l border-amare-border pl-3">{{ $item }}</li>@endforeach</ul><div class="space-y-2"><a href="{{ $contactLink['href'] }}" @if ($contactLink['isWhatsapp']) target="_blank" rel="noopener" @endif class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"><span class="link-editorial">{{ $package->cta_label }}</span></a><a href="{{ route('packages.show', $package->slug) }}" class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"><span class="link-editorial">Conhecer esta modalidade</span></a></div></li>
<li class="space-y-5 border-t border-amare-border pt-4" data-reveal data-reveal-from="up"><p class="text-xs font-semibold uppercase tracking-[.14em] text-amare-accent">{{ $package->level }}</p><h3 class="text-3xl font-medium">{{ $package->name }}</h3><p class="text-amare-muted">{{ $package->summary }}</p><ul class="space-y-2 text-sm text-amare-text-muted">@foreach ($package->scope_items as $item)<li class="border-l border-amare-border pl-3">{{ $item }}</li>@endforeach</ul><div class="space-y-2"><a href="{{ $contactLink['href'] }}" @if ($contactLink['isWhatsapp']) target="_blank" rel="noopener" @endif class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"><span class="border-b border-amare-accent pb-1">{{ $package->cta_label }}</span></a><a href="{{ route('packages.show', $package->slug) }}" class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"><span class="border-b border-amare-accent pb-1">Conhecer esta modalidade</span></a></div></li>
@endforeach
</ol>
@endif

View File

@@ -1,44 +0,0 @@
@props([
'seal' => 'Recebido',
'title',
'body',
'homeLabel' => 'Voltar para a home',
'whatsappLabel' => 'Falar no WhatsApp',
'whatsappText' => 'Olá, enviei um briefing e gostaria de acompanhar.',
])
@php
$settings = $siteSettings ?? \App\Models\SiteSetting::instance();
$digits = preg_replace('/\D/', '', (string) ($settings->whatsapp_number ?? ''));
$whatsappHref = strlen($digits) >= 10
? 'https://wa.me/'.$digits.'?text='.rawurlencode($whatsappText)
: null;
@endphp
<div
role="status"
data-form-success
class="mt-10 max-w-2xl space-y-6 border border-amare-border bg-amare-bg-deep px-6 py-10 md:px-10"
data-motion="page-open"
{{ $attributes }}
>
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent" data-motion-beat="seal">{{ $seal }}</p>
<h3 class="text-3xl font-medium tracking-tight text-amare-text md:text-4xl" data-motion-beat="title">{{ $title }}</h3>
<p class="max-w-xl text-lg leading-relaxed text-amare-muted" data-motion-beat="heading">{{ $body }}</p>
<div class="flex flex-col items-start gap-4 pt-2 sm:flex-row sm:items-center sm:gap-8" data-motion-beat="cta">
<a href="{{ route('home') }}" class="btn btn-primary min-h-11 px-6 text-xs font-bold uppercase tracking-[0.09em]">
{{ $homeLabel }}
</a>
@if ($whatsappHref)
<a
href="{{ $whatsappHref }}"
class="btn btn-ghost min-h-0 px-0 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"
rel="noopener noreferrer"
target="_blank"
>
<span class="link-editorial">{{ $whatsappLabel }}</span>
</a>
@endif
</div>
</div>

View File

@@ -5,7 +5,7 @@
} catch (\Throwable $e) {
$pageMeta = new \App\Application\Data\PageMeta(
title: 'Página não encontrada - Amare Assessoria',
description: 'Não encontramos este endereço. Volte à home e continue por ali.',
description: 'A página que você procura não existe ou foi movida.',
canonical: '',
robots: 'noindex, nofollow',
);
@@ -18,7 +18,7 @@
<div class="container-amare max-w-2xl space-y-6 py-20 text-center" data-motion="page-open" data-reveal-group>
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent" data-motion-beat="seal">Erro 404</p>
<h1 class="text-4xl font-medium tracking-tight text-amare-text" data-motion-beat="title">Página não encontrada</h1>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Não encontramos este endereço talvez tenha mudado de lugar. A home continua sendo o melhor ponto de partida.</p>
<p class="text-amare-muted" data-reveal data-reveal-from="up">O endereço que você tentou abrir não existe ou foi movido.</p>
<p data-reveal data-reveal-from="up">
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
Voltar para a home

View File

@@ -5,7 +5,7 @@
} catch (\Throwable $e) {
$pageMeta = new \App\Application\Data\PageMeta(
title: 'Sessão expirada - Amare Assessoria',
description: 'Sua sessão ficou quieta demais. Volte e envie de novo com calma.',
description: 'Sua sessão expirou. Volte e tente enviar novamente.',
canonical: '',
robots: 'noindex, nofollow',
);
@@ -18,7 +18,7 @@
<div class="container-amare max-w-2xl space-y-6 py-20 text-center" data-motion="page-open" data-reveal-group>
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent" data-motion-beat="seal">Erro 419</p>
<h1 class="text-4xl font-medium tracking-tight text-amare-text" data-motion-beat="title">Sessão expirada</h1>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Sua sessão ficou quieta demais e precisamos recomeçar. Volte à página anterior e envie de novo os dados ainda estão seguros.</p>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Sua sessão expirou. Volte e tente enviar novamente.</p>
<p data-reveal data-reveal-from="up">
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
Voltar para a home

View File

@@ -5,7 +5,7 @@
} catch (\Throwable $e) {
$pageMeta = new \App\Application\Data\PageMeta(
title: 'Muitas solicitações - Amare Assessoria',
description: 'Recebemos muitos pedidos em pouco tempo. Aguarde um instante e tente de novo.',
description: 'Você enviou muitas solicitações em pouco tempo. Aguarde um instante e tente novamente.',
canonical: '',
robots: 'noindex, nofollow',
);
@@ -18,7 +18,7 @@
<div class="container-amare max-w-2xl space-y-6 py-20 text-center" data-motion="page-open" data-reveal-group>
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent" data-motion-beat="seal">Erro 429</p>
<h1 class="text-4xl font-medium tracking-tight text-amare-text" data-motion-beat="title">Muitas solicitações</h1>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Recebemos vários pedidos em pouco tempo. Espere um instante assim que pudermos, tentamos de novo com calma.</p>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Você enviou muitas solicitações em pouco tempo. Aguarde um instante e tente novamente.</p>
<p data-reveal data-reveal-from="up">
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
Voltar para a home

View File

@@ -5,7 +5,7 @@
} catch (\Throwable $e) {
$pageMeta = new \App\Application\Data\PageMeta(
title: 'Algo deu errado - Amare Assessoria',
description: 'Algo saiu do roteiro do nosso lado. Tente novamente em instantes.',
description: 'Não foi possível concluir o pedido. Tente novamente em instantes.',
canonical: '',
robots: 'noindex, nofollow',
);
@@ -18,7 +18,7 @@
<div class="container-amare max-w-2xl space-y-6 py-20 text-center" data-motion="page-open" data-reveal-group>
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent" data-motion-beat="seal">Erro 500</p>
<h1 class="text-4xl font-medium tracking-tight text-amare-text" data-motion-beat="title">Algo deu errado</h1>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Algo saiu do roteiro do nosso lado. estamos cuidando disso tente novamente em instantes.</p>
<p class="text-amare-muted" data-reveal data-reveal-from="up">o foi possível concluir o pedido agora. Tente novamente em instantes.</p>
<p data-reveal data-reveal-from="up">
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
Voltar para a home

View File

@@ -5,7 +5,7 @@
} catch (\Throwable $e) {
$pageMeta = new \App\Application\Data\PageMeta(
title: 'Em manutenção - Amare Assessoria',
description: 'Estamos em uma pausa breve de manutenção. Voltamos em instantes.',
description: 'Estamos realizando uma breve manutenção. Tente novamente em instantes.',
canonical: '',
robots: 'noindex, nofollow',
);
@@ -18,7 +18,7 @@
<div class="container-amare max-w-2xl space-y-6 py-20 text-center" data-motion="page-open" data-reveal-group>
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent" data-motion-beat="seal">Erro 503</p>
<h1 class="text-4xl font-medium tracking-tight text-amare-text" data-motion-beat="title">Em manutenção</h1>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Estamos em uma pausa breve para deixar tudo no lugar. Voltamos em instantes obrigado pela paciência.</p>
<p class="text-amare-muted" data-reveal data-reveal-from="up">Estamos realizando uma breve manutenção. Tente novamente em instantes.</p>
<p data-reveal data-reveal-from="up">
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
Voltar para a home

View File

@@ -58,10 +58,10 @@
<header class="site-header sticky top-0 z-40 border-b border-amare-border bg-amare-bg/95 backdrop-blur-sm">
<div class="container-amare grid grid-cols-[auto_1fr_auto] items-center gap-4 py-4 md:grid-cols-[1fr_auto_1fr] md:h-[74px] md:py-0">
<nav id="main-nav" class="main-nav order-3 col-span-3 hidden flex-col gap-4 border-t border-amare-border pt-4 md:order-1 md:col-span-1 md:flex md:flex-row md:items-center md:gap-1 md:border-0 md:pt-0" aria-label="Principal" data-main-nav>
<a href="{{ url('/#casamentos') }}" class="nav-link-editorial btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:bg-transparent hover:text-amare-accent md:px-3">Casamentos</a>
<a href="{{ url('/#corporate') }}" class="nav-link-editorial btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:bg-transparent hover:text-amare-accent md:px-3">Corporate</a>
<a href="{{ route('portfolio.index') }}" class="nav-link-editorial btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:bg-transparent hover:text-amare-accent md:px-3">Portfólio</a>
<a href="{{ route('about') }}" class="nav-link-editorial btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:bg-transparent hover:text-amare-accent md:px-3">Amare</a>
<a href="{{ route('home') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Início</a>
<a href="{{ route('services.index') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Serviços</a>
<a href="{{ route('portfolio.index') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Portfólio</a>
<a href="{{ route('about') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Amare</a>
<a href="{{ route('briefing') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:hidden">Solicitar proposta</a>
</nav>
@@ -73,6 +73,7 @@
<a href="{{ route('briefing') }}" class="btn btn-outline hidden min-h-0 px-6 text-xs font-bold uppercase tracking-[0.09em] md:inline-flex">
Conte seu evento
</a>
</a>
<button
type="button"
@@ -110,11 +111,10 @@
$footerSocials = collect($siteSettings->social_links ?? [])->filter(static fn ($url) => filled($url));
@endphp
<div class="container-amare flex flex-col gap-10 py-14 md:py-[58px]">
<div class="flex flex-col gap-8 md:flex-row md:items-end md:justify-between">
<div class="container-amare flex flex-col gap-8 py-14 md:flex-row md:items-end md:justify-between md:py-[58px]">
<div class="space-y-3">
<x-brand.logo variant="on-light" class="h-10 w-auto" />
<p class="text-sm text-amare-muted">Boutique de assessoria &amp; produção &bull; São Paulo</p>
<p class="text-sm text-amare-muted">Assessoria &amp; produção de eventos &bull; São Paulo</p>
</div>
<p class="text-sm text-amare-muted md:text-right">
@@ -128,21 +128,12 @@
</p>
</div>
<nav aria-label="Rodapé" class="flex flex-col gap-4 border-t border-amare-border pt-8 text-sm text-amare-muted md:flex-row md:flex-wrap md:items-center md:gap-x-6 md:gap-y-2">
<a href="{{ url('/#casamentos') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Casamentos</a>
<a href="{{ url('/#corporate') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Corporate</a>
<a href="{{ route('services.index') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Serviços</a>
<a href="{{ route('portfolio.index') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Portfólio</a>
<a href="{{ route('about') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Sobre</a>
<a href="{{ route('briefing') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Briefing</a>
<a href="{{ route('contact') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Parcerias</a>
<a href="{{ route('privacy') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Privacidade</a>
</nav>
</div>
<div class="border-t border-amare-border">
<div class="container-amare flex flex-col gap-2 py-6 text-sm text-amare-muted md:flex-row md:items-center md:justify-between">
<p>&copy; {{ now()->year }} {{ $siteSettings->brand_name }}. Todos os direitos reservados.</p>
<p>
<a href="{{ route('privacy') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Política de privacidade</a>
</p>
</div>
</div>
</footer>

View File

@@ -42,11 +42,10 @@
</p>
@if (session('status') === 'briefing-sent')
<x-public.form-success
seal="Briefing recebido"
title="Recebemos sua ficha."
body="Vamos ler com atenção e retornar em breve pelo canal que você informou. Enquanto isso, pode seguir explorando o portfólio."
/>
<div role="status" class="alert mt-8 border border-amare-border bg-amare-bg-deep">
<p class="font-semibold text-amare-text">Mensagem enviada.</p>
<p class="mt-1 text-amare-muted">Recebemos seu briefing e retornaremos em breve pelo canal informado.</p>
</div>
@else
<form
method="POST"
@@ -80,7 +79,7 @@
autocomplete="name"
maxlength="120"
placeholder="Seu nome"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('nome') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('nome') input-error @enderror"
@error('nome') aria-invalid="true" aria-describedby="nome-error" @enderror
>
@error('nome')
@@ -99,7 +98,7 @@
autocomplete="email"
maxlength="254"
placeholder="voce@email.com"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('email') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('email') input-error @enderror"
@error('email') aria-invalid="true" aria-describedby="email-error" @enderror
>
@error('email')
@@ -118,7 +117,7 @@
autocomplete="tel"
maxlength="40"
placeholder="(11) 90000-0000"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('telefone') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('telefone') input-error @enderror"
@error('telefone') aria-invalid="true" aria-describedby="telefone-error" @enderror
>
@error('telefone')
@@ -132,7 +131,7 @@
id="tipo_evento"
name="tipo_evento"
required
class="select field-editorial w-full text-amare-text @error('tipo_evento') select-error @enderror"
class="select w-full text-amare-text @error('tipo_evento') select-error @enderror"
@error('tipo_evento') aria-invalid="true" aria-describedby="tipo_evento-error" @enderror
>
<option value="" selected disabled>Selecione...</option>
@@ -155,7 +154,7 @@
value="{{ old('data_periodo') }}"
maxlength="80"
placeholder="Ex.: novembro de 2027"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('data_periodo') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('data_periodo') input-error @enderror"
@error('data_periodo') aria-invalid="true" aria-describedby="data_periodo-error" @enderror
>
@error('data_periodo')
@@ -173,7 +172,7 @@
required
maxlength="80"
placeholder="Ex.: São Paulo"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('cidade') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('cidade') input-error @enderror"
@error('cidade') aria-invalid="true" aria-describedby="cidade-error" @enderror
>
@error('cidade')
@@ -193,7 +192,7 @@
inputmode="numeric"
autocomplete="off"
placeholder="Ex.: 120"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('convidados') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('convidados') input-error @enderror"
@error('convidados') aria-invalid="true" aria-describedby="convidados-error" @enderror
>
@error('convidados')
@@ -210,7 +209,7 @@
value="{{ old('servico_interesse', request('servico_interesse')) }}"
maxlength="120"
placeholder="Ex.: planejamento completo"
class="input field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('servico_interesse') input-error @enderror"
class="input w-full text-amare-text placeholder:text-amare-muted/60 @error('servico_interesse') input-error @enderror"
@error('servico_interesse') aria-invalid="true" aria-describedby="servico_interesse-error" @enderror
>
@error('servico_interesse')
@@ -228,7 +227,7 @@
rows="6"
maxlength="3000"
placeholder="Conte sobre o seu evento, expectativas e principais preocupações."
class="textarea field-editorial w-full text-amare-text placeholder:text-amare-muted/60 @error('mensagem') textarea-error @enderror"
class="textarea w-full text-amare-text placeholder:text-amare-muted/60 @error('mensagem') textarea-error @enderror"
@error('mensagem') aria-invalid="true" aria-describedby="mensagem-error" @enderror
>{{ old('mensagem') }}</textarea>
@error('mensagem')

View File

@@ -20,12 +20,7 @@
<div class="container-amare py-16 md:py-24" data-reveal-group>
<h2 id="partner-form-heading" class="text-3xl font-medium text-amare-text" data-reveal data-reveal-from="up">Apresente seu trabalho</h2>
@if (session('status') === 'partner-inquiry-sent')
<x-public.form-success
seal="Apresentação recebida"
title="Recebemos sua apresentação."
body="Vamos avaliar a aderência com o cuidado que cada parceria merece. Se fizer sentido, entraremos em contato."
whatsapp-text="Olá, enviei uma apresentação de parceria e gostaria de acompanhar."
/>
<div role="status" class="mt-8 border border-amare-border bg-amare-bg px-6 py-5"><p class="font-semibold">Mensagem enviada.</p><p class="mt-1 text-amare-muted">Recebemos sua apresentação e entraremos em contato se houver aderência.</p></div>
@else
<form method="POST" action="{{ route('contact.store') }}" class="mt-10 max-w-3xl space-y-8" data-contact-form data-reveal data-reveal-from="up">
@csrf
@@ -33,10 +28,10 @@
@if ($errors->any())<div role="alert" class="border border-amare-error/40 px-6 py-5">Revise os campos destacados abaixo.</div>@endif
<div class="grid gap-8 sm:grid-cols-2">
@foreach (['nome' => 'Nome *', 'empresa' => 'Nome profissional ou empresa', 'email' => 'E-mail *', 'atuacao' => 'Atuação ou serviço *', 'area_atendimento' => 'Área de atendimento *', 'portfolio_redes' => 'Portfólio ou redes', 'telefone' => 'Telefone'] as $field => $label)
<div><label class="mb-2 block text-xs font-semibold uppercase tracking-[.14em]" for="{{ $field }}">{{ $label }}</label><input class="field-editorial w-full border-0 border-b border-amare-border bg-transparent py-3 focus:border-amare-accent focus:outline-none @error($field) border-amare-error @enderror" id="{{ $field }}" name="{{ $field }}" value="{{ old($field) }}" maxlength="{{ $field === 'portfolio_redes' ? 500 : ($field === 'email' ? 254 : 160) }}" @required(in_array($field, ['nome','email','atuacao','area_atendimento'], true)) @error($field) aria-invalid="true" @enderror>@error($field)<p class="mt-2 text-sm text-amare-error">{{ $message }}</p>@enderror</div>
<div><label class="mb-2 block text-xs font-semibold uppercase tracking-[.14em]" for="{{ $field }}">{{ $label }}</label><input class="w-full border-0 border-b border-amare-border bg-transparent py-3 focus:border-amare-accent focus:outline-none @error($field) border-amare-error @enderror" id="{{ $field }}" name="{{ $field }}" value="{{ old($field) }}" maxlength="{{ $field === 'portfolio_redes' ? 500 : ($field === 'email' ? 254 : 160) }}" @required(in_array($field, ['nome','email','atuacao','area_atendimento'], true)) @error($field) aria-invalid="true" @enderror>@error($field)<p class="mt-2 text-sm text-amare-error">{{ $message }}</p>@enderror</div>
@endforeach
</div>
<div><label class="mb-2 block text-xs font-semibold uppercase tracking-[.14em]" for="mensagem">Mensagem *</label><textarea class="field-editorial w-full border border-amare-border bg-transparent p-3 focus:border-amare-accent focus:outline-none" id="mensagem" name="mensagem" rows="6" required maxlength="3000">{{ old('mensagem') }}</textarea>@error('mensagem')<p class="mt-2 text-sm text-amare-error">{{ $message }}</p>@enderror</div>
<div><label class="mb-2 block text-xs font-semibold uppercase tracking-[.14em]" for="mensagem">Mensagem *</label><textarea class="w-full border border-amare-border bg-transparent p-3 focus:border-amare-accent focus:outline-none" id="mensagem" name="mensagem" rows="6" required maxlength="3000">{{ old('mensagem') }}</textarea>@error('mensagem')<p class="mt-2 text-sm text-amare-error">{{ $message }}</p>@enderror</div>
<p class="max-w-2xl text-sm text-amare-muted">Ao enviar, você reconhece o uso destes dados exclusivamente para analisar uma possível parceria, conforme a <a class="text-amare-accent underline" href="{{ route('privacy') }}">política de privacidade</a>.</p>
<button type="submit" data-submit-button class="btn btn-primary text-sm font-semibold uppercase tracking-[.12em]">Enviar apresentação</button>
</form>

View File

@@ -5,38 +5,9 @@
<div class="border-b border-amare-border bg-amare-bg-deep">
<div class="container-amare space-y-10 py-16 md:py-24">
<nav aria-label="Vertentes do portfólio" class="flex flex-wrap gap-x-6 gap-y-2 border-b border-amare-border pb-6 text-sm">
<a
href="{{ route('portfolio.index') }}"
@class([
'inline-flex min-h-11 items-center uppercase tracking-[0.08em] transition-colors',
'font-semibold text-amare-accent' => $vertical === null,
'text-amare-muted hover:text-amare-accent' => $vertical !== null,
])
>Todos</a>
@foreach (\App\Domain\Marketing\PortfolioVertical::cases() as $option)
<a
href="{{ route('portfolio.index', ['vertente' => $option->value]) }}"
@class([
'inline-flex min-h-11 items-center uppercase tracking-[0.08em] transition-colors',
'font-semibold text-amare-accent' => $vertical === $option,
'text-amare-muted hover:text-amare-accent' => $vertical !== $option,
])
>{{ $option->label() }}</a>
@endforeach
</nav>
@if ($cases->isEmpty())
@if ($vertical === \App\Domain\Marketing\PortfolioVertical::Corporate)
<div class="flex min-h-[280px] flex-col items-center justify-center gap-4 bg-amare-bg p-10 text-center">
<p class="text-xs font-bold uppercase tracking-[0.16em] text-amare-accent-deep">Portfólio Corporate</p>
<h2 class="text-[clamp(1.5625rem,2.7vw,2.125rem)] font-medium leading-tight text-amare-text">Conteúdo em construção</h2>
<p class="max-w-lg text-amare-text-muted">Ainda não publicamos cases corporativos autorizados. Em vez de inventar prova, mantemos este espaço pronto para projetos reais.</p>
<a href="{{ route('briefing') }}" class="btn btn-outline mt-2 text-xs font-bold uppercase tracking-[0.09em]">Falar sobre um evento corporativo</a>
</div>
@else
<p class="text-amare-muted">Novos casos serão publicados assim que o acervo estiver organizado. Enquanto isso, fale conosco para conhecer o nosso trabalho.</p>
@endif
@else
@php
$hasPrioritizedImage = false;
@@ -82,9 +53,6 @@
@endphp
@endif
<div class="space-y-2 border-t border-amare-border pt-4">
@if (filled($case->event_type))
<p class="text-xs font-bold uppercase tracking-[0.16em] text-amare-accent-deep">{{ $case->event_type }}</p>
@endif
<h2 class="text-2xl font-medium text-amare-text">
<a href="{{ route('portfolio.show', $case->slug) }}" class="transition-colors hover:text-amare-accent">{{ $case->title }}</a>
</h2>

View File

@@ -13,7 +13,7 @@
Conhecer os serviços
</a>
<a href="{{ route('contact') }}" class="btn btn-ghost min-h-0 px-0 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent hover:bg-transparent hover:text-amare-accent-deep">
<span class="link-editorial">Tenho um evento em mente</span>
<span class="border-b border-amare-accent pb-1">Tenho um evento em mente</span>
</a>
</div>
</x-public.photo-hero>
@@ -65,7 +65,7 @@
<p class="text-amare-text-muted">{{ $package->compare_summary }}</p>
</div>
<a href="{{ route('packages.show', $package->slug) }}" class="btn btn-ghost min-h-0 self-start px-0 text-xs font-semibold uppercase tracking-[0.09em] text-amare-accent hover:bg-transparent hover:text-amare-accent-deep">
<span class="link-editorial">Conhecer esta modalidade</span>
<span class="border-b border-amare-accent pb-1">Conhecer esta modalidade</span>
</a>
</article>
@endif

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Tests\Feature\Application\Queries\Marketing;
use App\Application\Queries\Marketing\GetPublishedPortfolioCases;
use App\Domain\Marketing\PortfolioVertical;
use App\Models\PortfolioCase;
use App\Models\PortfolioImage;
use Illuminate\Foundation\Testing\RefreshDatabase;
@@ -59,26 +58,4 @@ class GetPublishedPortfolioCasesTest extends TestCase
$this->assertTrue($cases->get(0)?->relationLoaded('images'));
$this->assertCount(2, $cases->get(0)?->images ?? []);
}
public function test_filters_published_cases_by_portfolio_vertical(): void
{
PortfolioCase::factory()->published()->create([
'title' => 'Wedding Case',
'event_type' => 'Mini wedding',
'sort_order' => 10,
]);
PortfolioCase::factory()->published()->create([
'title' => 'Corporate Case',
'event_type' => 'Evento corporativo',
'sort_order' => 20,
]);
$weddings = (new GetPublishedPortfolioCases)(PortfolioVertical::Casamentos);
$corporate = (new GetPublishedPortfolioCases)(PortfolioVertical::Corporate);
$this->assertCount(1, $weddings);
$this->assertSame('Wedding Case', $weddings->first()?->title);
$this->assertCount(1, $corporate);
$this->assertSame('Corporate Case', $corporate->first()?->title);
}
}

View File

@@ -89,28 +89,6 @@ class ContactBriefingTest extends TestCase
});
}
public function test_successful_submission_shows_editorial_confirmation(): void
{
SiteSetting::instance()->update(['whatsapp_number' => '+55 11 98888-7777']);
Mail::fake();
$response = $this
->from(route('briefing'))
->followingRedirects()
->post(route('briefing.store'), $this->validPayload());
$response
->assertOk()
->assertSee('data-form-success', false)
->assertSee('Briefing recebido')
->assertSee('Recebemos sua ficha')
->assertSee('role="status"', false)
->assertSee('data-motion-beat="seal"', false)
->assertSee(route('home'), false)
->assertSee('https://wa.me/5511988887777', false)
->assertDontSee('<form', false);
}
public function test_submission_normalizes_email_and_phone_before_reaching_mailables(): void
{
$settings = SiteSetting::instance();

View File

@@ -72,19 +72,7 @@ class HeritageEditorialTokensTest extends TestCase
{
$vite = (string) file_get_contents(base_path('vite.config.js'));
$this->assertMatchesRegularExpression(
"/^import \\{ fontsource \\} from 'laravel-vite-plugin\\/fonts';/m",
$vite,
);
$this->assertDoesNotMatchRegularExpression(
'/^import \\{[^}]*\\bbunny\\b/m',
$vite,
);
$this->assertDoesNotMatchRegularExpression(
'/^import \\{[^}]*\\bgoogle\\b/m',
$vite,
);
$this->assertStringContainsString("fontsource('EB Garamond'", $vite);
$this->assertStringContainsString("bunny('EB Garamond'", $vite);
$this->assertStringNotContainsString('Instrument Sans', $vite);
}

View File

@@ -96,9 +96,6 @@ class MotionMarkupTest extends TestCase
$this->assertStringContainsString('[data-reveal]', $appCss);
$this->assertStringNotContainsString('[data-chapter-index]', $appCss);
$this->assertStringContainsString('prefers-reduced-motion: no-preference', $appCss);
$this->assertStringContainsString('.field-editorial', $appCss);
$this->assertStringContainsString('.nav-link-editorial', $appCss);
$this->assertStringContainsString('.link-editorial', $appCss);
// Entrance motion must not fade text opacity — mid-fade fails WCAG contrast (axe).
$this->assertDoesNotMatchRegularExpression(

View File

@@ -66,22 +66,6 @@ class PartnerInquiryTest extends TestCase
Mail::assertQueued(PartnerInquiry::class, 1);
}
public function test_successful_partner_inquiry_shows_editorial_confirmation(): void
{
SiteSetting::instance();
Mail::fake();
$this
->from(route('contact'))
->followingRedirects()
->post(route('contact.store'), $this->validPayload())
->assertOk()
->assertSee('data-form-success', false)
->assertSee('Apresentação recebida')
->assertSee('Recebemos sua apresentação')
->assertDontSee('name="atuacao"', false);
}
public function test_partner_honeypot_reports_success_without_sending_mail(): void
{
SiteSetting::instance();

View File

@@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\PublicSite;
use App\Models\PortfolioCase;
use App\Models\SiteSetting;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PortfolioVerticalFilterTest extends TestCase
{
use RefreshDatabase;
public function test_portfolio_index_filters_by_vertical_and_shows_corporate_empty_state(): void
{
SiteSetting::instance();
PortfolioCase::factory()->published()->create([
'title' => 'Casamento Jardim',
'slug' => 'casamento-jardim',
'event_type' => 'Casamento',
'sort_order' => 10,
]);
PortfolioCase::factory()->published()->create([
'title' => 'Convenção Anual',
'slug' => 'convencao-anual',
'event_type' => 'Corporativo',
'sort_order' => 20,
]);
$this->get(route('portfolio.index'))
->assertOk()
->assertSee('Casamento Jardim')
->assertSee('Convenção Anual')
->assertSee('Vertentes do portfólio')
->assertSee('Casamentos')
->assertSee('Corporate');
$this->get(route('portfolio.index', ['vertente' => 'casamentos']))
->assertOk()
->assertSee('Casamento Jardim')
->assertDontSee('Convenção Anual');
$this->get(route('portfolio.index', ['vertente' => 'corporate']))
->assertOk()
->assertSee('Convenção Anual')
->assertDontSee('Casamento Jardim');
PortfolioCase::query()->where('slug', 'convencao-anual')->update(['published_at' => null]);
$this->get(route('portfolio.index', ['vertente' => 'corporate']))
->assertOk()
->assertSee('Portfólio Corporate')
->assertSee('Conteúdo em construção')
->assertDontSee('Convenção Anual');
}
}

View File

@@ -54,8 +54,7 @@ class PublicLayoutSeoTest extends TestCase
->assertSee('Amare Brand')
->assertSee('hello@amare.test')
->assertSee('(11) 91111-1111')
->assertSee('Privacidade')
->assertSee(route('privacy'), false)
->assertSee('Política de privacidade')
->assertSee('https://instagram.com/amare', false);
}
}

View File

@@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\PublicSite;
use App\Models\SiteSetting;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PublicNavigationVerticalsTest extends TestCase
{
use RefreshDatabase;
public function test_header_and_footer_expose_casamentos_and_corporate_without_implying_weddings_only(): void
{
SiteSetting::instance();
$response = $this->get(route('about'))->assertOk();
$homeCasamentos = url('/#casamentos');
$homeCorporate = url('/#corporate');
$response
->assertSee('id="main-nav"', false)
->assertSee('href="'.$homeCasamentos.'"', false)
->assertSee('>Casamentos</a>', false)
->assertSee('href="'.$homeCorporate.'"', false)
->assertSee('>Corporate</a>', false)
->assertSee('href="'.route('portfolio.index').'"', false)
->assertSee('href="'.route('about').'"', false)
->assertSee('href="'.route('services.index').'"', false)
->assertSee('href="'.route('privacy').'"', false)
->assertSee('aria-label="Rodapé"', false)
->assertSeeInOrder([
'aria-label="Rodapé"',
'href="'.$homeCasamentos.'"',
'Casamentos',
'href="'.$homeCorporate.'"',
'Corporate',
], false);
}
}

View File

@@ -1,6 +1,6 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { fontsource } from 'laravel-vite-plugin/fonts';
import { bunny } from 'laravel-vite-plugin/fonts';
import tailwindcss from '@tailwindcss/vite';
/**
@@ -76,7 +76,7 @@ export default defineConfig({
],
refresh: true,
fonts: [
fontsource('EB Garamond', {
bunny('EB Garamond', {
weights: [400, 500, 600],
}),
],