Compare commits

..

1 Commits

Author SHA1 Message Date
d17ab82055 feat: remodelar /sobre conforme mock editorial
All checks were successful
CI / static (pull_request) Successful in 2m17s
CI / unit (pull_request) Successful in 3m18s
CI / feature (pull_request) Successful in 2m42s
CI / container (pull_request) Successful in 1m4s
CI / browser (pull_request) Successful in 4m0s
Alinha a página Sobre à composição aprovada (hero, pilares, Michele, processo, portfólio, CTA), com foto da Michele via CMS e tokens Heritage Editorial.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 17:25:35 -03:00
44 changed files with 182 additions and 734 deletions

View File

@@ -1,19 +0,0 @@
## WHAT
<!-- What changed? -->
## WHY
<!-- Why this change? -->
## HOW
<!-- How was it implemented? Include verification. -->
## Linear Issue
<!-- Required. Identifier + URL, e.g. MAN-133 https://linear.app/maneco-workspace/issue/MAN-133 -->
## Comments
<!-- Notes for reviewers. Screenshots, follow-ups, out of scope. -->

View File

@@ -1,19 +0,0 @@
## WHAT
<!-- What changed? -->
## WHY
<!-- Why this change? -->
## HOW
<!-- How was it implemented? Include verification. -->
## Linear Issue
<!-- Required. Identifier + URL, e.g. MAN-133 https://linear.app/maneco-workspace/issue/MAN-133 -->
## Comments
<!-- Notes for reviewers. Screenshots, follow-ups, out of scope. -->

View File

@@ -18,19 +18,7 @@ Feature and browser tests require the `amare_test` PostgreSQL database configure
## Worktrees
Always work in a git worktree created from the `main` ref — never modify `main` directly and never commit from the primary working tree. Create a dedicated worktree per feature/branch with `git worktree add -b <branch> <path> main`. Include the Linear identifier in the branch name (e.g. `docs/man-132-…`). When the SDLC gate below is green, commit and open the PR without asking. Watch CI until green, then merge. Clean up the worktree with `git worktree remove` after merge.
## Agent SDLC (do not ask)
Never ask whether to commit or open a PR. After each complete slice of work, ship it:
1. **Work is done** in a Linear-linked worktree.
2. **Tests are written or updated** for the changed layer. Skip new tests only when the change has no runtime impact (docs, templates, static config).
3. **Pre-commit passes** (`composer pint:check` and `composer phpstan`). Do not `--no-verify`. If the hook fails, fix and rerun.
4. **Commit** with Conventional Commits. Cite the Linear identifier.
5. **Push and open the Gitea PR** (`tea pulls create`) using `.gitea/PULL_REQUEST_TEMPLATE.md` (WHAT / WHY / HOW / Linear Issue / Comments). Attach the PR URL on the Linear issue via `save_issue` `links`. GitHub is the legacy mirror only.
Do not wait for "pode commitar?" or "abre o PR?".
Always work in a git worktree created from the `main` ref — never modify `main` directly and never commit from the primary working tree. Create a dedicated worktree per feature/branch with `git worktree add -b <branch> <path> main`. On finishing work, create a PR, watch CI until green, then merge it. Clean up the worktree with `git worktree remove` after merge.
## Git Hooks (husky)
@@ -53,7 +41,7 @@ Tests use Pest 4; browser coverage uses Pest Browser/Playwright. Tests are verif
## Commit & Pull Request Guidelines
History follows Conventional Commit-style subjects, for example `feat: Fase 0 — Fundação`. Use `<type>: <imperative summary>` (`feat`, `fix`, `docs`, `test`, `chore`) and keep commits focused. Fill the PR template (WHAT / WHY / HOW / Linear Issue / Comments). **Linear Issue is required** (identifier + URL). An OpenSpec change is extra context, not a substitute. Include verification commands and screenshots for UI changes. Ensure all CI jobs pass. After pre-commit is green, commit and open the PR — do not ask.
History follows Conventional Commit-style subjects, for example `feat: Fase 0 — Fundação`. Use `<type>: <imperative summary>` (`feat`, `fix`, `docs`, `test`, `chore`) and keep commits focused. Pull requests should explain scope, link the relevant issue or OpenSpec requirement, list verification commands, and include screenshots for UI changes. Ensure all CI jobs pass.
## Security & Configuration
@@ -71,8 +59,6 @@ The design system lives in `DESIGN.md` (palette, typography, layout, do's and do
Issues live in Linear, driven through the Linear MCP tools. See `docs/agents/issue-tracker.md` for workspace, team, and tool conventions. The repo ships no `.mcp.json`, so the Linear MCP has to be enabled for the session before those tools exist — if it isn't, report that instead of silently falling back to another tracker.
**Every piece of work and every PR must be tied to a Linear issue.** Do not create a branch, worktree, or PR until an issue exists (identifier like `MAN-132`). If the user did not give one, search Linear first; if none fits, create it with `save_issue` on team `Maneco-workspace` before starting. Put the identifier in the branch name. Cite identifier + URL in the PR body. After opening the PR, attach the PR URL on the issue via `save_issue` `links`. Do not start untracked work.
### Triage labels
Default vocabulary: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.

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

@@ -17,16 +17,6 @@ Issues and specs for this repo live in Linear. Use the Linear MCP tools for all
- **Link work**: `save_issue` with `project`, `cycle`, `parentId`, `blocks` / `blockedBy` (relations are append-only).
- **Cross-link to a PR**: attach the PR URL via `save_issue` `links`.
## Required: issue before work and before PR
Every branch, worktree, and pull request in this repo must be tied to a Linear issue.
1. **Starting work:** require an identifier (e.g. `MAN-132`). If the user did not give one, search Linear. If none fits, create the issue with `save_issue` on team `Maneco-workspace` *before* creating a branch or worktree.
2. **Branch:** include the identifier (`docs/man-132-…`, `feat/man-126-…`).
3. **PR:** cite identifier + URL in the body. An OpenSpec change is extra context, not a substitute.
4. **After the PR exists:** attach the PR URL on the issue via `save_issue` `links`. Do this as part of the automatic SDLC in `AGENTS.md` — do not ask before opening the PR.
5. If Linear MCP is unavailable, stop and report — do not start untracked work.
## When a skill says "publish to the issue tracker"
Create a Linear issue with `save_issue` on team `Maneco-workspace`.

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

@@ -2,13 +2,19 @@
'settings',
])
<section aria-labelledby="about-cta-heading" class="border-b border-amare-border bg-amare-bg-deep py-20 md:py-24" data-about-cta data-reveal-group>
<div class="container-amare max-w-4xl space-y-6" data-reveal data-reveal-from="up">
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent">Próximo passo</p>
<h2 id="about-cta-heading" class="text-headline font-medium text-amare-text">Vamos criar algo inesquecível juntos?</h2>
<p class="max-w-2xl text-amare-muted">Conte com a Amare para transformar seu evento em uma experiência organizada, cuidadosa e memorável.</p>
<div>
<a href="{{ route('briefing') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
<section aria-labelledby="about-cta-heading" class="border-b border-amare-border bg-amare-bg py-6 md:py-7" data-about-cta>
<div class="container-amare grid items-center gap-7 md:grid-cols-[auto_minmax(0,1fr)_auto]" data-reveal-group>
<svg class="mx-auto h-11 w-11 text-amare-accent md:mx-0" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true" data-reveal data-reveal-from="up">
<rect x="7" y="12" width="34" height="26" rx="2"/>
<path d="m8 15 16 13 16-13"/>
<path d="M18 12v-2a6 6 0 0 1 12 0v2"/>
</svg>
<div class="text-center md:text-left" data-reveal data-reveal-from="up">
<h2 id="about-cta-heading" class="text-[clamp(1.75rem,3vw,2rem)] font-medium text-amare-accent-deep">Vamos criar algo inesquecível juntos?</h2>
<p class="mt-1 text-base leading-relaxed text-amare-text">Conte com a Amare para transformar seu evento em uma experiência organizada, cuidadosa e memorável.</p>
</div>
<div class="justify-self-center md:justify-self-end" data-reveal data-reveal-from="up">
<a href="{{ route('briefing') }}" class="btn btn-primary text-xs font-bold uppercase tracking-[0.11em]">
{{ $settings->hero_cta_label ?: 'Solicitar proposta' }}
</a>
</div>

View File

@@ -7,9 +7,9 @@
@endphp
<section id="michele" aria-labelledby="founder-heading" class="border-b border-amare-border bg-amare-bg" data-about-founder>
<div class="grid min-h-[500px] lg:grid-cols-12" data-reveal-group>
<div class="grid min-h-[500px] lg:grid-cols-2" data-reveal-group>
@if ($hasImage)
<div class="min-h-[420px] overflow-hidden bg-amare-bg-deep max-lg:aspect-[4/5] lg:col-span-6 lg:min-h-full" data-reveal-media>
<div class="min-h-[420px] overflow-hidden bg-amare-bg-deep lg:min-h-full" data-reveal-media>
<x-media.image
:path="$settings->founder_image_path"
:alt="$settings->founder_image_alt ?: 'Michele, da Amare'"
@@ -18,20 +18,20 @@
/>
</div>
@else
<div class="flex min-h-[420px] items-center justify-center bg-amare-bg-deep lg:col-span-6 lg:min-h-full" data-founder-tonal>
<div class="flex min-h-[420px] items-center justify-center bg-amare-bg-deep lg:min-h-full" data-founder-tonal>
<p class="px-4 text-center text-xs font-semibold uppercase tracking-[0.16em] text-amare-muted">Retrato da Michele</p>
</div>
@endif
<div class="flex flex-col justify-center px-6 py-16 lg:col-span-6 lg:px-[clamp(3rem,6vw,7rem)]" data-reveal data-reveal-from="up">
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent">A pessoa por trás da experiência</p>
<h2 id="founder-heading" class="mt-3 text-headline font-medium text-amare-text">Conheça Michele</h2>
<div class="flex flex-col justify-center px-6 py-12 md:px-12 md:py-16 lg:px-[70px]" data-reveal data-reveal-from="up">
<p class="text-sm font-medium uppercase tracking-[0.16em] text-amare-accent">A pessoa por trás da experiência</p>
<h2 id="founder-heading" class="mt-3 text-[clamp(2rem,3vw,2.55rem)] font-medium text-amare-accent-deep">Conheça Michele</h2>
<p class="mt-5 max-w-[520px] text-lg leading-relaxed text-amare-text">Michele representa o olhar humano da Amare: escuta, organização e presença durante todo o processo de construção do evento.</p>
<p class="mt-3.5 max-w-[520px] text-lg leading-relaxed text-amare-text-muted">Seu papel é transformar necessidades e escolhas em um planejamento claro, cuidando dos detalhes sem perder de vista a experiência de quem contrata e de quem participa.</p>
<div class="mt-8 flex max-w-[500px] flex-col items-start justify-between gap-4 border border-amare-border bg-amare-bg-deep px-5 py-4 sm:flex-row sm:items-center">
<p class="mt-3.5 max-w-[520px] text-lg leading-relaxed text-amare-text">Seu papel é transformar necessidades e escolhas em um planejamento claro, cuidando dos detalhes sem perder de vista a experiência de quem contrata e de quem participa.</p>
<div class="mt-5 flex max-w-[500px] flex-col items-start justify-between gap-4 bg-amare-bg-deep px-4 py-3.5 sm:flex-row sm:items-center">
<div>
<strong class="block text-[2rem] font-normal italic text-amare-text">Michele</strong>
<small class="text-xs font-semibold uppercase tracking-[0.08em] text-amare-muted">Assessoria e produção de eventos</small>
<strong class="block text-[2rem] font-normal italic text-amare-accent-deep">Michele</strong>
<small class="text-[0.65rem] uppercase tracking-[0.08em] text-amare-muted">Assessoria e produção de eventos</small>
</div>
<a href="{{ route('briefing') }}" class="btn btn-outline shrink-0 text-xs font-bold uppercase tracking-[0.11em]">Falar com a Michele</a>
</div>

View File

@@ -9,20 +9,18 @@
<section aria-labelledby="about-hero-heading" class="border-b border-amare-border bg-amare-bg" data-about-hero data-motion="page-open">
@if ($hasImage)
<div class="grid min-h-[440px] lg:grid-cols-12">
<div class="flex min-w-0 items-center bg-amare-bg px-6 py-16 lg:col-span-5 lg:px-[clamp(3rem,6vw,7rem)]" data-reveal-group>
<div class="w-full max-w-[470px] space-y-6">
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent" data-motion-beat="seal">Sobre nós</p>
<h1 id="about-hero-heading" class="max-w-[470px] whitespace-normal break-normal text-hero-spread font-medium text-amare-text" data-motion-beat="title">Sobre a Amare</h1>
<p class="max-w-[450px] text-[clamp(1.25rem,2vw,1.55rem)] italic leading-snug text-amare-text-muted" data-motion-beat="lede">{{ $lead }}</p>
<span class="block h-9 w-px bg-amare-accent" aria-hidden="true"></span>
<p class="max-w-[430px] text-lg leading-relaxed text-amare-text">A Amare une sensibilidade, organização e atenção aos detalhes para criar experiências bem planejadas, do primeiro encontro à execução do evento.</p>
<div data-motion-beat="cta">
<a href="#michele" class="btn btn-outline text-xs font-bold uppercase tracking-[0.11em]">Conhecer mais</a>
</div>
<div class="container-amare grid min-h-[440px] lg:grid-cols-[minmax(0,0.85fr)_minmax(0,1.45fr)]">
<div class="flex flex-col items-start justify-center py-12 md:py-14 lg:py-16 lg:pr-10" data-reveal-group>
<p class="text-sm font-medium uppercase tracking-[0.16em] text-amare-accent" data-motion-beat="seal">Sobre nós</p>
<h1 id="about-hero-heading" class="mt-3 max-w-[470px] text-[clamp(3rem,5vw,5rem)] font-medium leading-[0.96] tracking-tight text-amare-accent-deep" data-motion-beat="title">Sobre a Amare</h1>
<p class="mt-4 max-w-[450px] font-serif text-[clamp(1.25rem,2vw,1.55rem)] italic leading-snug text-amare-muted" data-motion-beat="lede">{{ $lead }}</p>
<span class="my-6 block h-9 w-px bg-amare-accent" aria-hidden="true"></span>
<p class="mb-7 max-w-[430px] text-lg leading-relaxed text-amare-text">A Amare une sensibilidade, organização e atenção aos detalhes para criar experiências bem planejadas, do primeiro encontro à execução do evento.</p>
<div data-motion-beat="cta">
<a href="#michele" class="btn btn-outline text-xs font-bold uppercase tracking-[0.11em]">Conhecer mais</a>
</div>
</div>
<div class="min-h-[360px] overflow-hidden bg-amare-bg-deep max-lg:aspect-[4/5] lg:col-span-7 lg:min-h-0" data-motion-beat="media" data-reveal-media>
<div class="min-h-[360px] overflow-hidden bg-amare-bg-deep lg:min-h-full" data-motion-beat="media" data-reveal-media>
<x-media.image
:path="$settings->about_image_path"
:alt="$settings->about_image_alt ?: 'Sobre a Amare'"
@@ -36,9 +34,9 @@
@else
<div class="container-amare py-16 md:py-24" data-tonal-hero>
<div class="max-w-[470px] space-y-5" data-motion-beat="heading">
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent" data-motion-beat="seal">Sobre nós</p>
<h1 id="about-hero-heading" class="text-headline font-medium tracking-tight text-amare-text" data-motion-beat="title">Sobre a Amare</h1>
<p class="text-lg italic leading-snug text-amare-text-muted" data-motion-beat="lede">{{ $lead }}</p>
<p class="text-sm font-medium uppercase tracking-[0.16em] text-amare-accent" data-motion-beat="seal">Sobre nós</p>
<h1 id="about-hero-heading" class="text-headline font-medium tracking-tight text-amare-accent-deep" data-motion-beat="title">Sobre a Amare</h1>
<p class="text-lg italic leading-snug text-amare-muted" data-motion-beat="lede">{{ $lead }}</p>
<span class="block h-9 w-px bg-amare-accent" aria-hidden="true"></span>
<p class="text-lg leading-relaxed text-amare-text">A Amare une sensibilidade, organização e atenção aos detalhes para criar experiências bem planejadas, do primeiro encontro à execução do evento.</p>
<div data-motion-beat="cta">

View File

@@ -1,30 +1,30 @@
<section aria-label="Pilares da Amare" class="border-b border-amare-border bg-amare-bg" data-about-pillars>
<div class="container-amare py-16 md:py-24" data-reveal-group>
<div class="grid border-l border-t border-amare-border md:grid-cols-3">
<article class="flex flex-col gap-4 border-b border-r border-amare-border p-8 text-center md:p-10" data-reveal data-reveal-from="up">
<svg class="mx-auto h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<div class="container-amare" data-reveal-group>
<div class="grid md:grid-cols-3">
<article class="relative border-t border-amare-border px-6 py-8 text-center md:border-t-0 md:px-10" data-reveal data-reveal-from="up">
<svg class="mx-auto mb-2.5 h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<circle cx="18" cy="16" r="6"/>
<circle cx="30" cy="16" r="6"/>
<path d="M7 37c1-8 5-12 11-12s10 4 11 12M21 37c1-8 5-12 11-12 5 0 9 4 10 12"/>
</svg>
<h3 class="text-base font-medium uppercase tracking-[0.05em] text-amare-text">Atendimento próximo</h3>
<p class="mx-auto max-w-[280px] text-amare-text-muted">Escuta atenta para compreender prioridades, contexto e expectativas de cada evento.</p>
<h3 class="text-base font-medium uppercase tracking-[0.05em] text-amare-accent-deep">Atendimento próximo</h3>
<p class="mx-auto mt-2 max-w-[280px] text-[0.95rem] leading-relaxed text-amare-text">Escuta atenta para compreender prioridades, contexto e expectativas de cada evento.</p>
</article>
<article class="flex flex-col gap-4 border-b border-r border-amare-border p-8 text-center md:p-10" data-reveal data-reveal-from="up">
<svg class="mx-auto h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<article class="relative border-t border-amare-border px-6 py-8 text-center md:border-t-0 md:px-10 md:before:absolute md:before:inset-y-[30px] md:before:left-0 md:before:w-px md:before:bg-amare-border" data-reveal data-reveal-from="up">
<svg class="mx-auto mb-2.5 h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<rect x="10" y="7" width="28" height="34" rx="2"/>
<path d="M17 15h14M17 22h14M17 29h8M29 29h2"/>
</svg>
<h3 class="text-base font-medium uppercase tracking-[0.05em] text-amare-text">Planejamento minucioso</h3>
<p class="mx-auto max-w-[280px] text-amare-text-muted">Organização clara das etapas para transformar decisões em uma execução consistente.</p>
<h3 class="text-base font-medium uppercase tracking-[0.05em] text-amare-accent-deep">Planejamento minucioso</h3>
<p class="mx-auto mt-2 max-w-[280px] text-[0.95rem] leading-relaxed text-amare-text">Organização clara das etapas para transformar decisões em uma execução consistente.</p>
</article>
<article class="flex flex-col gap-4 border-b border-r border-amare-border p-8 text-center md:p-10" data-reveal data-reveal-from="up">
<svg class="mx-auto h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<article class="relative border-t border-amare-border px-6 py-8 text-center md:border-t-0 md:px-10 md:before:absolute md:before:inset-y-[30px] md:before:left-0 md:before:w-px md:before:bg-amare-border" data-reveal data-reveal-from="up">
<svg class="mx-auto mb-2.5 h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<path d="M24 40C10 32 8 20 8 12c8 0 14 4 16 11 2-7 8-11 16-11 0 8-2 20-16 28Z"/>
<path d="M24 23v17"/>
</svg>
<h3 class="text-base font-medium uppercase tracking-[0.05em] text-amare-text">Execução tranquila</h3>
<p class="mx-auto max-w-[280px] text-amare-text-muted">Cuidado com a operação para que anfitriões e convidados possam viver o momento.</p>
<h3 class="text-base font-medium uppercase tracking-[0.05em] text-amare-accent-deep">Execução tranquila</h3>
<p class="mx-auto mt-2 max-w-[280px] text-[0.95rem] leading-relaxed text-amare-text">Cuidado com a operação para que anfitriões e convidados possam viver o momento.</p>
</article>
</div>
</div>

View File

@@ -3,58 +3,53 @@
])
@php
$slots = collect([
['label' => 'Eventos sociais', 'tall' => true],
['label' => 'Celebrações', 'tall' => false],
['label' => 'Detalhes', 'tall' => false],
['label' => 'Sociais', 'tall' => false],
['label' => 'Corporativo', 'tall' => false],
['label' => 'Produção', 'tall' => false],
]);
$slots = [
'Eventos sociais',
'Celebrações',
'Detalhes',
'Sociais',
'Corporativo',
'Produção',
];
$cases = $cases->take(6)->values();
@endphp
<section id="portfolio" aria-labelledby="about-portfolio-heading" class="border-b border-amare-border bg-amare-bg" data-about-portfolio>
<div class="container-amare py-16 md:py-24" data-reveal-group>
<div class="grid gap-10 lg:grid-cols-12 lg:items-end" data-reveal data-reveal-from="up">
<div class="lg:col-span-5">
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent">Seleção de trabalhos</p>
<h2 id="about-portfolio-heading" class="mt-3 text-headline font-medium text-amare-text">Portfólio em destaque</h2>
</div>
<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>
</a>
</div>
<div class="container-amare grid lg:grid-cols-[315px_minmax(0,1fr)]" data-reveal-group>
<div class="flex flex-col justify-center px-6 py-11 md:px-10" data-reveal data-reveal-from="up">
<p class="text-sm font-medium uppercase tracking-[0.16em] text-amare-accent">Seleção de trabalhos</p>
<h2 id="about-portfolio-heading" class="mt-3 text-[clamp(1.75rem,3vw,2.05rem)] font-medium leading-tight text-amare-accent-deep">Portfólio em destaque</h2>
<p class="mt-4 max-w-sm text-base leading-relaxed text-amare-text">Uma composição de eventos sociais e corporativos para apresentar a versatilidade da Amare.</p>
<a href="{{ route('portfolio.index') }}" class="btn btn-outline mt-5 self-start text-xs font-bold uppercase tracking-[0.11em]">Ver portfólio completo</a>
</div>
<div class="mt-12 grid grid-cols-1 gap-[14px] sm:grid-cols-2 lg:grid-cols-[1.2fr_0.8fr_0.8fr]" aria-label="Projetos em destaque" data-editorial-portfolio data-reveal-group>
@foreach ($slots as $index => $slot)
<div class="grid auto-rows-[150px] grid-cols-2 gap-1 py-1 lg:auto-rows-[118px] lg:grid-cols-12" aria-label="Projetos em destaque" data-reveal-group>
@foreach ($slots as $index => $label)
@php
$case = $cases->get($index);
$span = match ($index) {
0 => 'lg:col-span-5',
1 => 'lg:col-span-4',
2 => 'lg:col-span-3 lg:row-span-2 min-h-[190px] lg:min-h-0',
3 => 'lg:col-span-4',
4 => 'lg:col-span-3',
default => 'lg:col-span-2',
};
@endphp
<div
@class([
'overflow-hidden bg-amare-bg-deep',
'sm:col-span-2 sm:min-h-[420px] lg:col-span-1 lg:row-span-2 lg:min-h-[634px]' => $slot['tall'],
'min-h-[220px] sm:min-h-[310px]' => ! $slot['tall'],
])
data-reveal
data-reveal-from="up"
>
<div @class(['relative min-h-[150px] overflow-hidden bg-amare-bg-deep', $span]) data-reveal data-reveal-from="up">
@if ($case && filled($case->cover_image_path))
<a href="{{ route('portfolio.show', $case) }}" class="block h-full w-full" aria-label="{{ $case->title }}">
<x-media.image
:path="$case->cover_image_path"
:alt="$case->cover_image_alt ?: $case->title"
sizes="(max-width: 1023px) 100vw, 33vw"
class="img-editorial h-full w-full object-cover transition-opacity hover:opacity-90"
sizes="(max-width: 1023px) 50vw, 25vw"
class="img-editorial h-full w-full object-cover transition-transform duration-[450ms] ease-out hover:scale-[1.03]"
/>
<span class="absolute bottom-3 left-3 bg-amare-accent-deep/90 px-3 py-1.5 text-[0.57rem] font-semibold uppercase tracking-[0.06em] text-amare-accent-text">{{ $label }}</span>
</a>
@else
<div class="flex h-full w-full items-center justify-center">
<p class="px-4 text-center text-xs font-semibold uppercase tracking-[0.16em] text-amare-muted">{{ $slot['label'] }}</p>
<div class="flex h-full w-full items-end justify-start p-3">
<span class="bg-amare-accent-deep/90 px-3 py-1.5 text-[0.57rem] font-semibold uppercase tracking-[0.06em] text-amare-accent-text">{{ $label }}</span>
</div>
@endif
</div>

View File

@@ -1,29 +1,29 @@
<section aria-labelledby="process-heading" class="border-b border-amare-border bg-amare-bg" data-about-process>
<div class="container-amare py-16 md:py-24" data-reveal-group>
<h2 id="process-heading" class="mb-10 text-center text-headline font-medium text-amare-text" data-reveal data-reveal-from="up">Como trabalhamos</h2>
<div class="grid border-l border-t border-amare-border md:grid-cols-3">
<article class="flex flex-col gap-3 border-b border-r border-amare-border p-8 text-center md:p-10" data-reveal data-reveal-from="up">
<section aria-labelledby="process-heading" class="border-b border-amare-border bg-amare-bg py-5 md:py-6" data-about-process>
<div class="container-amare" data-reveal-group>
<h2 id="process-heading" class="mb-3 text-center text-[clamp(1.75rem,3vw,2rem)] font-medium text-amare-accent-deep" data-reveal data-reveal-from="up">Como trabalhamos</h2>
<div class="grid md:grid-cols-3">
<article class="relative border-t border-amare-border px-6 py-4 text-center md:border-t-0 md:px-10" data-reveal data-reveal-from="up">
<svg class="mx-auto h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<path d="M15 29c-5-4-6-12-2-17 5-7 17-7 22 0 4 6 2 14-4 18-2 1-3 4-3 7H20c0-4-2-6-5-8Z"/>
<path d="M20 41h8"/>
</svg>
<h3 class="text-base font-medium uppercase tracking-[0.04em] text-amare-text">Escuta e entendimento</h3>
<p class="mx-auto max-w-[280px] text-amare-text-muted">Começamos pelo contexto do evento, suas prioridades, estilo e expectativas.</p>
<h3 class="mt-2 text-base font-medium uppercase tracking-[0.04em] text-amare-accent-deep">Escuta e entendimento</h3>
<p class="mx-auto mt-1.5 max-w-[280px] text-[0.94rem] leading-relaxed text-amare-text">Começamos pelo contexto do evento, suas prioridades, estilo e expectativas.</p>
</article>
<article class="flex flex-col gap-3 border-b border-r border-amare-border p-8 text-center md:p-10" data-reveal data-reveal-from="up">
<article class="relative border-t border-amare-border px-6 py-4 text-center md:border-t-0 md:px-10 md:before:absolute md:before:inset-y-3 md:before:left-0 md:before:w-px md:before:bg-amare-border" data-reveal data-reveal-from="up">
<svg class="mx-auto h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<rect x="8" y="11" width="32" height="29" rx="2"/>
<path d="M15 7v8M33 7v8M8 20h32M15 26h4M23 26h4M31 26h3M15 33h4M23 33h4"/>
</svg>
<h3 class="text-base font-medium uppercase tracking-[0.04em] text-amare-text">Planejamento e curadoria</h3>
<p class="mx-auto max-w-[280px] text-amare-text-muted">Organizamos etapas, fornecedores e decisões para manter cada frente alinhada.</p>
<h3 class="mt-2 text-base font-medium uppercase tracking-[0.04em] text-amare-accent-deep">Planejamento e curadoria</h3>
<p class="mx-auto mt-1.5 max-w-[280px] text-[0.94rem] leading-relaxed text-amare-text">Organizamos etapas, fornecedores e decisões para manter cada frente alinhada.</p>
</article>
<article class="flex flex-col gap-3 border-b border-r border-amare-border p-8 text-center md:p-10" data-reveal data-reveal-from="up">
<article class="relative border-t border-amare-border px-6 py-4 text-center md:border-t-0 md:px-10 md:before:absolute md:before:inset-y-3 md:before:left-0 md:before:w-px md:before:bg-amare-border" data-reveal data-reveal-from="up">
<svg class="mx-auto h-[34px] w-[34px] text-amare-muted" viewBox="0 0 48 48" fill="none" stroke="currentColor" aria-hidden="true">
<path d="M24 39S8 30 8 18c0-6 4-10 10-10 4 0 7 2 9 6 2-4 5-6 9-6 6 0 10 4 10 10 0 12-22 21-22 21Z"/>
</svg>
<h3 class="text-base font-medium uppercase tracking-[0.04em] text-amare-text">Execução e experiência</h3>
<p class="mx-auto max-w-[280px] text-amare-text-muted">Coordenamos a operação para que o evento aconteça com fluidez e presença.</p>
<h3 class="mt-2 text-base font-medium uppercase tracking-[0.04em] text-amare-accent-deep">Execução e experiência</h3>
<p class="mx-auto mt-1.5 max-w-[280px] text-[0.94rem] leading-relaxed text-amare-text">Coordenamos a operação para que o evento aconteça com fluidez e presença.</p>
</article>
</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,39 +111,29 @@
$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="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>
</div>
<p class="text-sm text-amare-muted md:text-right">
@if ($footerSocials->isEmpty())
Instagram &middot; WhatsApp &middot; E-mail &middot; LinkedIn (quando confirmado)
@else
@foreach ($footerSocials as $network => $url)
<a href="{{ $url }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent" rel="noopener noreferrer" target="_blank">{{ $footerSocialLabel((string) $network) }}</a>@unless ($loop->last) &nbsp;&middot;&nbsp; @endunless
@endforeach
@endif
</p>
<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">Assessoria &amp; produção de eventos &bull; São Paulo</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>
<p class="text-sm text-amare-muted md:text-right">
@if ($footerSocials->isEmpty())
Instagram &middot; WhatsApp &middot; E-mail &middot; LinkedIn (quando confirmado)
@else
@foreach ($footerSocials as $network => $url)
<a href="{{ $url }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent" rel="noopener noreferrer" target="_blank">{{ $footerSocialLabel((string) $network) }}</a>@unless ($loop->last) &nbsp;&middot;&nbsp; @endunless
@endforeach
@endif
</p>
</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
<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>
@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>
@@ -47,7 +47,8 @@
:show-intro="false"
kicker="tag"
:show-subtitle="true"
:band-cta-href="route('briefing')"
cta-route="contact"
:band-cta-href="route('contact')"
band-body="Conte um pouco sobre o casamento. A Amare entende o momento de vocês e orienta o melhor formato de acompanhamento sem depender de um quiz automático."
:show-note="false"
/>
@@ -65,7 +66,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

@@ -44,33 +44,6 @@ class AboutPageContentTest extends TestCase
->assertDontSee('Personalização sem complicação desnecessária');
}
public function test_about_layout_matches_site_editorial_rhythm(): void
{
SiteSetting::instance()->update([
'about_image_path' => 'content/heroes/about.jpg',
'about_image_alt' => 'Estúdio Amare',
]);
$html = $this->get(route('about'))->assertOk()->getContent();
$this->assertMatchesRegularExpression(
'/data-about-hero[\s\S]*?lg:grid-cols-12[\s\S]*?lg:col-span-5[\s\S]*?text-hero-spread[\s\S]*?lg:col-span-7/',
$html,
);
$this->assertStringNotContainsString('data-photo-hero', $html);
$this->assertDoesNotMatchRegularExpression(
'/data-about-hero[\s\S]*?min-h-\[calc\(100dvh-5rem\)\]/',
$html,
);
$this->assertMatchesRegularExpression('/data-about-pillars[\s\S]*?py-16 md:py-24/', $html);
$this->assertMatchesRegularExpression('/data-about-process[\s\S]*?py-16 md:py-24/', $html);
$this->assertMatchesRegularExpression('/data-about-portfolio[\s\S]*?data-editorial-portfolio/', $html);
$this->assertDoesNotMatchRegularExpression('/data-about-portfolio[\s\S]*?rounded-full/', $html);
$this->assertMatchesRegularExpression('/<(?:section)[^>]*(?:data-about-cta[^>]*bg-amare-bg-deep|bg-amare-bg-deep[^>]*data-about-cta)/', $html);
$this->assertStringContainsString('Próximo passo', $html);
}
public function test_about_renders_founder_image_from_cms_when_configured(): void
{
SiteSetting::instance()->update([

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,53 +0,0 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\PublicSite;
use App\Models\SiteSetting;
use App\Models\WeddingPackage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ServicesPageCtaTest extends TestCase
{
use RefreshDatabase;
public function test_package_cta_uses_whatsapp_when_a_number_is_configured(): void
{
SiteSetting::instance()->update(['whatsapp_number' => '+55 11 98888-7777']);
WeddingPackage::factory()->published()->create([
'name' => 'Grand Jour',
'cta_label' => 'Quero conhecer a Grand Jour',
]);
$response = $this->get(route('services.index'));
$response
->assertOk()
->assertSee(
'https://wa.me/5511988887777?text='.rawurlencode('Olá, gostaria de conversar sobre a modalidade Grand Jour para meu casamento.'),
false,
)
->assertSee('target="_blank"', false)
->assertDontSee(route('briefing', ['servico_interesse' => 'Grand Jour']), false);
}
public function test_package_cta_falls_back_to_briefing_without_whatsapp_number(): void
{
SiteSetting::instance()->update(['whatsapp_number' => null]);
WeddingPackage::factory()->published()->create([
'name' => 'Essenza',
'cta_label' => 'Quero conhecer a Essenza',
]);
$response = $this->get(route('services.index'));
$response
->assertOk()
->assertSee('href="'.route('briefing', ['servico_interesse' => 'Essenza']).'"', false)
->assertDontSee('wa.me/', 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],
}),
],