Compare commits
5 Commits
feat/man-1
...
feat/man-1
| Author | SHA1 | Date | |
|---|---|---|---|
| 900b76ad19 | |||
| 9ee9b51b0c | |||
| 220143390e | |||
| fd3a4eb5f1 | |||
| 3207b7e568 |
@@ -79,4 +79,4 @@ Default vocabulary: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-
|
|||||||
|
|
||||||
### Domain docs
|
### Domain docs
|
||||||
|
|
||||||
Single-context repo. There is no `CONTEXT.md` — the domain is documented in `SPEC.md` (§8 is the domain model and database schema) and `PRODUCT.md`, with current capabilities described per-capability under `openspec/specs/`. `docs/adr/README.md` is an index only: ADR-001 through ADR-010 are decided in `SPEC.md` §21, and there are no standalone ADR files. `docs/agents/domain.md` describes the generic `CONTEXT.md`/`CONTEXT-MAP.md` layout that the engineering skills look for and instructs them to proceed silently when it's absent, which is the case here.
|
Single-context repo. There is no `CONTEXT.md` on purpose — **`SPEC.md` §8.0 is the ubiquitous-language SSOT** (domain model/schema continue in §8.1+). `PRODUCT.md` covers positioning; capabilities live under `openspec/specs/`. `docs/adr/README.md` is an index only: ADR-001 through ADR-010 are decided in `SPEC.md` §21. `docs/agents/domain.md` tells skills to use `SPEC.md` §8 instead of inventing a parallel `CONTEXT.md`.
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ Site-wide content is a singleton row reached via `SiteSetting::instance()`. Publ
|
|||||||
- **Livewire/Filament temp uploads are pinned to the `local` disk** when `FILESYSTEM_DISK=r2`, because the S3 driver would make the browser PUT straight to R2 and hit CORS. Final media still lands on `r2` via `App\Support\PublicImageUploadRules`. Set `LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK` explicitly to override.
|
- **Livewire/Filament temp uploads are pinned to the `local` disk** when `FILESYSTEM_DISK=r2`, because the S3 driver would make the browser PUT straight to R2 and hit CORS. Final media still lands on `r2` via `App\Support\PublicImageUploadRules`. Set `LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK` explicitly to override.
|
||||||
- **Contact form is rate limited**: named limiter `contact-briefing`, 5/min per IP, registered in `AppServiceProvider` and applied in `routes/web.php`.
|
- **Contact form is rate limited**: named limiter `contact-briefing`, 5/min per IP, registered in `AppServiceProvider` and applied in `routes/web.php`.
|
||||||
- **Filament 5 nested resource layout**: resources are split into `app/Filament/Resources/<Resource>/{Pages,Schemas,Tables,RelationManagers}` rather than a flat resource class. Follow the existing shape in `Resources/PortfolioCases/`.
|
- **Filament 5 nested resource layout**: resources are split into `app/Filament/Resources/<Resource>/{Pages,Schemas,Tables,RelationManagers}` rather than a flat resource class. Follow the existing shape in `Resources/PortfolioCases/`.
|
||||||
- **Everything user-facing is pt-BR**: routes are `/servicos`, `/portfolio`, `/portfolio/{slug}`, `/sobre`, `/privacidade`, `/contato`. `APP_LOCALE=pt_BR`, `APP_TIMEZONE=America/Sao_Paulo` (`config/app.php:68`).
|
- **Everything user-facing is pt-BR**: routes are `/servicos`, `/pacotes/{slug}`, `/portfolio`, `/portfolio/{slug}`, `/sobre`, `/briefing`, `/contato`, `/privacidade`. `APP_LOCALE=pt_BR`, `APP_TIMEZONE=America/Sao_Paulo` (`config/app.php:68`). Glossário canônico: `SPEC.md` §8.0.
|
||||||
- **Design tokens** live in `resources/css/tokens.css` (Heritage Editorial; see `DESIGN.md`). `tests/Feature/PublicSite/HeritageEditorialTokensTest.php` reads that file and asserts the exact hex values, `EB Garamond`, zero border radii, `--amare-container-max: 1120px`, and the *absence* of shadow tokens — so any token edit is a deliberate test change too. Motion lives in `resources/js/motion.js` and is asserted by `tests/Feature/PublicSite/MotionMarkupTest.php` + `tests/Browser/MotionTest.php`.
|
- **Design tokens** live in `resources/css/tokens.css` (Heritage Editorial; see `DESIGN.md`). `tests/Feature/PublicSite/HeritageEditorialTokensTest.php` reads that file and asserts the exact hex values, `EB Garamond`, zero border radii, `--amare-container-max: 1120px`, and the *absence* of shadow tokens — so any token edit is a deliberate test change too. Motion lives in `resources/js/motion.js` and is asserted by `tests/Feature/PublicSite/MotionMarkupTest.php` + `tests/Browser/MotionTest.php`.
|
||||||
|
|
||||||
## Navigating the normative docs
|
## Navigating the normative docs
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ No lançamento, o site público premium e o CMS interno formam a superfície ent
|
|||||||
## Operating Context
|
## Operating Context
|
||||||
|
|
||||||
- Atuação em São Paulo, capital.
|
- Atuação em São Paulo, capital.
|
||||||
- Serviços: casamentos, eventos corporativos e celebrações.
|
- Serviços: casamentos, eventos corporativos e celebrações. No site, casamento aparece como três **modalidades** (Essenza, Conduzione, Grand Jour); preço e limites comerciais ficam na proposta, não na página.
|
||||||
- Operação realizada por assessora principal e equipe pequena.
|
- Operação realizada por assessora principal e equipe pequena.
|
||||||
- Jornada de lançamento: descoberta no site, briefing por e-mail e contato comercial fora da plataforma.
|
- Jornada de lançamento: descoberta no site, briefing por e-mail e contato comercial fora da plataforma.
|
||||||
- Jornada futura prevista: qualificação do lead, pipeline comercial, conversão em evento, checklist, fornecedores, orçamento, pagamentos manuais e documentos.
|
- Jornada futura prevista: qualificação do lead, pipeline comercial, conversão em evento, checklist, fornecedores, orçamento, pagamentos manuais e documentos.
|
||||||
|
|||||||
69
SPEC.md
69
SPEC.md
@@ -283,11 +283,15 @@ Uma funcionalidade fora do MVP só poderá entrar quando:
|
|||||||
| Método | Rota | Nome sugerido | Finalidade |
|
| Método | Rota | Nome sugerido | Finalidade |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| GET | `/` | `home` | Home editorial |
|
| GET | `/` | `home` | Home editorial |
|
||||||
| GET | `/servicos` | `services.index` | Lista de serviços publicados |
|
| GET | `/servicos` | `services.index` | Lista de serviços / modalidades publicadas |
|
||||||
|
| GET | `/pacotes/{slug}` | `packages.show` | Detalhe de uma modalidade de casamento |
|
||||||
| GET | `/portfolio` | `portfolio.index` | Lista de casos publicados |
|
| GET | `/portfolio` | `portfolio.index` | Lista de casos publicados |
|
||||||
| GET | `/portfolio/{slug}` | `portfolio.show` | Detalhe de caso |
|
| GET | `/portfolio/{slug}` | `portfolio.show` | Detalhe de caso |
|
||||||
| GET | `/sobre` | `about` | História, método e credenciais |
|
| GET | `/sobre` | `about` | História, método e credenciais |
|
||||||
| GET | `/contato` | `contact` | Briefing de contato |
|
| GET | `/briefing` | `briefing` | Briefing comercial (pedido de proposta) |
|
||||||
|
| POST | `/briefing` | `briefing.store` | Envio do briefing comercial |
|
||||||
|
| GET | `/contato` | `contact` | Contato de parceiro / fornecedor (não é briefing) |
|
||||||
|
| POST | `/contato` | `contact.store` | Envio da consulta de parceiro |
|
||||||
| GET | `/privacidade` | `privacy` | Política de privacidade |
|
| GET | `/privacidade` | `privacy` | Política de privacidade |
|
||||||
| GET | `/sitemap.xml` | `sitemap` | Sitemap público |
|
| GET | `/sitemap.xml` | `sitemap` | Sitemap público |
|
||||||
| GET | `/robots.txt` | `robots` | Política de crawling |
|
| GET | `/robots.txt` | `robots` | Política de crawling |
|
||||||
@@ -324,7 +328,8 @@ Administração
|
|||||||
|
|
||||||
- Dashboard: `Filament Page` customizada com widgets orientados a exceção.
|
- Dashboard: `Filament Page` customizada com widgets orientados a exceção.
|
||||||
- Detalhe do evento: página customizada do Resource com resumo operacional.
|
- Detalhe do evento: página customizada do Resource com resumo operacional.
|
||||||
- Briefing público: Blade + Controller (`POST /contato`), ver §11.2.
|
- Briefing comercial: Blade + Controller (`POST /briefing`), ver §11.2.
|
||||||
|
- Contato de parceiro: Blade + Controller (`POST /contato`) — canal separado do briefing.
|
||||||
- Home: Blade com componentes de design reutilizáveis.
|
- Home: Blade com componentes de design reutilizáveis.
|
||||||
|
|
||||||
### 5.4 Decisões de UX YAGNI
|
### 5.4 Decisões de UX YAGNI
|
||||||
@@ -1243,6 +1248,35 @@ Auditoria não precisa ser um event sourcing. Registrar apenas operações crít
|
|||||||
|
|
||||||
## 8. Modelo de domínio e banco de dados
|
## 8. Modelo de domínio e banco de dados
|
||||||
|
|
||||||
|
Esta seção é a **fonte única de verdade** do vocabulário de domínio do produto. Issues Linear, OpenSpec, código e copy devem usar estes termos. Não criar glossário paralelo em `CONTEXT.md`.
|
||||||
|
|
||||||
|
### 8.0 Linguagem ubíqua (glossário)
|
||||||
|
|
||||||
|
**Vertente**:
|
||||||
|
Frente de marca da Amare — **Casamentos** ou **Corporate**.
|
||||||
|
_Avoid_: linha de negócio, vertical de produto (em copy pública), “área”
|
||||||
|
|
||||||
|
**Modalidade**:
|
||||||
|
Pacote/formato de acompanhamento de casamento gerenciado no CMS (`WeddingPackage`: Essenza, Conduzione, Grand Jour).
|
||||||
|
_Avoid_: pacote (em copy pública preferir “modalidade”), serviço genérico, plano
|
||||||
|
|
||||||
|
**Briefing**:
|
||||||
|
Pedido comercial de um potencial cliente descrevendo o evento e solicitando atendimento/proposta. Rota canônica: `/briefing`.
|
||||||
|
_Avoid_: contato (quando o sentido é pedido comercial), formulário de parceiro
|
||||||
|
|
||||||
|
**Contato parceiro**:
|
||||||
|
Consulta de fornecedor ou proposta de parceria. Rota canônica: `/contato`. Não mistura com Briefing.
|
||||||
|
_Avoid_: briefing, lead comercial
|
||||||
|
|
||||||
|
**CTA contextual**:
|
||||||
|
Link de conversão de uma Modalidade escolhida — WhatsApp com mensagem que nomeia a modalidade, ou fallback para `/briefing?servico_interesse=` quando não há número oficial válido.
|
||||||
|
|
||||||
|
**CTA orientação**:
|
||||||
|
Link de conversão para visitante que ainda não escolheu Modalidade — WhatsApp com mensagem de orientação humana (sem quiz), ou fallback para `/briefing` quando não há número oficial válido.
|
||||||
|
|
||||||
|
**Boutique**:
|
||||||
|
Conceito de posicionamento (cuidado, especialização, múltiplos serviços sob a mesma marca) — não é entidade de domínio nem rota.
|
||||||
|
|
||||||
### 8.1 Módulos
|
### 8.1 Módulos
|
||||||
|
|
||||||
| Módulo | Responsabilidade |
|
| Módulo | Responsabilidade |
|
||||||
@@ -1254,6 +1288,12 @@ Auditoria não precisa ser um event sourcing. Registrar apenas operações crít
|
|||||||
| Finance | Orçamento e pagamentos manuais |
|
| Finance | Orçamento e pagamentos manuais |
|
||||||
| Identity | Usuários, acesso e auditoria |
|
| Identity | Usuários, acesso e auditoria |
|
||||||
|
|
||||||
|
Vocabulário público vs persistência (Marketing):
|
||||||
|
|
||||||
|
- **Modalidade** é o nome público de cada oferta de acompanhamento de casamento. O visitante vê o nome próprio (Essenza, Conduzione, Grand Jour) e a tag de momento (Completa, Parcial, Final). Persistida em `wedding_packages`.
|
||||||
|
- **Vertente** é o contexto da marca (Casamentos vs Corporate). A tabela `services` descreve vertentes do catálogo, não as três modalidades.
|
||||||
|
- **Pacote**, no sentido de SKU com preço e limites operacionais, não é superfície pública. Preço, equipe no dia, teto de fornecedores, quilometragem e ferramentas de terceiros (incluindo gestão de convidados) ficam na proposta, não no registro público da modalidade.
|
||||||
|
|
||||||
### 8.2 Tabelas obrigatórias
|
### 8.2 Tabelas obrigatórias
|
||||||
|
|
||||||
#### `users`
|
#### `users`
|
||||||
@@ -1314,6 +1354,27 @@ Singleton:
|
|||||||
- timestamps;
|
- timestamps;
|
||||||
- soft delete opcional somente se recuperação for necessária.
|
- soft delete opcional somente se recuperação for necessária.
|
||||||
|
|
||||||
|
#### `wedding_packages`
|
||||||
|
|
||||||
|
Modalidades públicas de casamento (não confundir com `services`):
|
||||||
|
|
||||||
|
- `id`;
|
||||||
|
- `name`;
|
||||||
|
- `slug` unique;
|
||||||
|
- `level` (rótulo de momento, ex. `01 / COMPLETA`);
|
||||||
|
- `tag` nullable (ex. Assessoria completa);
|
||||||
|
- `subtitle` nullable;
|
||||||
|
- `summary`;
|
||||||
|
- `scope_items` jsonb (bullets de conceito nos cards);
|
||||||
|
- `cta_label`;
|
||||||
|
- `compare_heading` nullable;
|
||||||
|
- `compare_summary` nullable;
|
||||||
|
- campos editoriais da página `/pacotes/{slug}` (eyebrow, título, hero, benefícios, incluso em alto nível, audiência, CTA final, meta) — todos opcionais salvo o que a publicação exigir;
|
||||||
|
- `sort_order` integer indexed;
|
||||||
|
- `published_at` timestamp nullable indexed;
|
||||||
|
- timestamps;
|
||||||
|
- **sem** colunas de preço, centavos, equipe, limites de fornecedores/convidados/km ou ferramentas de terceiros.
|
||||||
|
|
||||||
#### `portfolio_cases`
|
#### `portfolio_cases`
|
||||||
|
|
||||||
- `id`;
|
- `id`;
|
||||||
@@ -1795,7 +1856,7 @@ Não transformar seções estáticas em componentes Livewire. Usar Blade quando
|
|||||||
|
|
||||||
### 11.2 Formulário de briefing
|
### 11.2 Formulário de briefing
|
||||||
|
|
||||||
> **Estado atual:** o formulário é implementado em Blade + Controller (`POST /contato`, `ContactBriefingRequest`), conforme WEB-05, e essa é a abordagem aceita — não um estágio provisório. Os requisitos abaixo valem independentemente da tecnologia; a criação de Lead segue para a Fase 2.
|
> **Estado atual:** o formulário de **Briefing** é Blade + Controller (`GET/POST /briefing`, `ContactBriefingRequest`), conforme WEB-05. A rota `/contato` é o canal de **Contato parceiro** (consulta não persistente), separado do briefing. A criação de Lead comercial persistente segue para a Fase 2.
|
||||||
|
|
||||||
O formulário deve:
|
O formulário deve:
|
||||||
|
|
||||||
|
|||||||
@@ -8,20 +8,45 @@ use App\Models\SiteSetting;
|
|||||||
|
|
||||||
final class PackageContactLink
|
final class PackageContactLink
|
||||||
{
|
{
|
||||||
|
public const ORIENTATION_MESSAGE = 'Olá, ainda não sei qual modalidade de assessoria faz sentido para o meu casamento e gostaria de orientação.';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the contextual contact link for a wedding package modality.
|
* Resolve the contextual contact link for a wedding package modality.
|
||||||
*
|
*
|
||||||
* @return array{href: string, isWhatsapp: bool}
|
* @return array{href: string, isWhatsapp: bool}
|
||||||
*/
|
*/
|
||||||
public static function for(SiteSetting $settings, string $packageName): array
|
public static function for(SiteSetting $settings, string $packageName): array
|
||||||
|
{
|
||||||
|
return self::resolve(
|
||||||
|
$settings,
|
||||||
|
'Olá, gostaria de conversar sobre a modalidade '.$packageName.' para meu casamento.',
|
||||||
|
['servico_interesse' => $packageName],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WhatsApp (or briefing fallback) when the visitor does not know which modality to choose.
|
||||||
|
*
|
||||||
|
* @return array{href: string, isWhatsapp: bool}
|
||||||
|
*/
|
||||||
|
public static function forOrientation(SiteSetting $settings): array
|
||||||
|
{
|
||||||
|
return self::resolve($settings, self::ORIENTATION_MESSAGE, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $briefingQuery
|
||||||
|
* @return array{href: string, isWhatsapp: bool}
|
||||||
|
*/
|
||||||
|
private static function resolve(SiteSetting $settings, string $whatsappMessage, array $briefingQuery): array
|
||||||
{
|
{
|
||||||
$digits = preg_replace('/\D/', '', (string) $settings->whatsapp_number);
|
$digits = preg_replace('/\D/', '', (string) $settings->whatsapp_number);
|
||||||
$isWhatsapp = strlen($digits) >= 10;
|
$isWhatsapp = strlen($digits) >= 10;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'href' => $isWhatsapp
|
'href' => $isWhatsapp
|
||||||
? 'https://wa.me/'.$digits.'?text='.rawurlencode('Olá, gostaria de conversar sobre a modalidade '.$packageName.' para meu casamento.')
|
? 'https://wa.me/'.$digits.'?text='.rawurlencode($whatsappMessage)
|
||||||
: route('briefing', ['servico_interesse' => $packageName]),
|
: route('briefing', $briefingQuery),
|
||||||
'isWhatsapp' => $isWhatsapp,
|
'isWhatsapp' => $isWhatsapp,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ use Illuminate\Database\Seeder;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wedding package modalities shown on the home page (03 — Amare Casamentos)
|
* Wedding package modalities shown on the home page, services page and
|
||||||
* and the services page. Copy is approved from the structural model preview
|
* `/pacotes/{slug}`. Public copy follows "Apresentação dos serviços Amare"
|
||||||
* (preview(2).html) and remains editable via the Filament resource. Detail
|
* (conceito e momento). Commercial limits, price and third-party tools stay
|
||||||
* copy follows the complete-assessoria preview
|
* off this seeder and off the public site.
|
||||||
* (amare-assessoria-completa.html) adapted to each modality.
|
|
||||||
*/
|
*/
|
||||||
class WeddingPackagesSeeder extends Seeder
|
class WeddingPackagesSeeder extends Seeder
|
||||||
{
|
{
|
||||||
@@ -27,141 +26,147 @@ class WeddingPackagesSeeder extends Seeder
|
|||||||
'slug' => 'essenza',
|
'slug' => 'essenza',
|
||||||
'level' => '01 / COMPLETA',
|
'level' => '01 / COMPLETA',
|
||||||
'tag' => 'Assessoria completa',
|
'tag' => 'Assessoria completa',
|
||||||
'subtitle' => 'Do planejamento ao grande dia.',
|
'subtitle' => 'Do primeiro planejamento ao grande dia.',
|
||||||
'summary' => 'Para casais que desejam contar com a Amare desde o início ou ainda estão estruturando etapas importantes.',
|
'summary' => 'Ideal para casais que desejam contar com a Amare desde o início da organização ou que ainda estão estruturando etapas importantes do casamento.',
|
||||||
'scope_items' => [
|
'scope_items' => [
|
||||||
'Estruturação do planejamento',
|
'Estruturação e acompanhamento do planejamento',
|
||||||
'Cronogramas, prazos e prioridades',
|
'Organização de cronogramas, prazos e prioridades',
|
||||||
'Orientação e gestão de fornecedores',
|
'Orientação na contratação e gestão de fornecedores',
|
||||||
'Alinhamento entre os envolvidos',
|
'Acompanhamento das etapas do projeto',
|
||||||
'Coordenação da execução',
|
'Organização e conferência de contratos e informações',
|
||||||
|
'Alinhamento entre casal, fornecedores e demais envolvidos',
|
||||||
|
'Construção do cronograma do grande dia',
|
||||||
|
'Coordenação e acompanhamento da execução do evento',
|
||||||
],
|
],
|
||||||
'cta_label' => 'Quero conhecer a Essenza',
|
'cta_label' => 'Quero conhecer a Essenza',
|
||||||
'compare_heading' => 'Começar com a Amare',
|
'compare_heading' => 'Começar com a Amare',
|
||||||
'compare_summary' => 'Acompanhamento mais amplo ao longo do planejamento.',
|
'compare_summary' => 'Acompanhamento do primeiro planejamento ao grande dia.',
|
||||||
'sort_order' => 1,
|
'sort_order' => 1,
|
||||||
'eyebrow' => 'Assessoria',
|
'eyebrow' => 'Assessoria',
|
||||||
'title_line' => 'Assessoria',
|
'title_line' => 'Assessoria',
|
||||||
'title_emphasis' => 'Completa',
|
'title_emphasis' => 'Completa',
|
||||||
'hero_lead' => 'Do planejamento ao grande dia, com calma, precisão e cuidado.',
|
'hero_lead' => 'A assessoria completa acompanha o projeto de forma estratégica e próxima, desde a construção do planejamento até a execução do evento.',
|
||||||
'benefits' => [
|
'benefits' => [
|
||||||
['icon_key' => 'calendar', 'label' => 'Organização integral'],
|
['icon_key' => 'calendar', 'label' => 'Planejamento organizado'],
|
||||||
['icon_key' => 'checklist', 'label' => 'Planejamento personalizado'],
|
['icon_key' => 'checklist', 'label' => 'Decisões mais seguras'],
|
||||||
['icon_key' => 'users', 'label' => 'Fornecedores selecionados'],
|
['icon_key' => 'users', 'label' => 'Fornecedores alinhados'],
|
||||||
['icon_key' => 'heart', 'label' => 'Experiência leve e inesquecível'],
|
['icon_key' => 'heart', 'label' => 'Grande dia com leveza'],
|
||||||
],
|
],
|
||||||
'included_items' => [
|
'included_items' => [
|
||||||
['icon_key' => 'heart', 'title' => 'Escuta e briefing', 'description' => 'Cada casal é único; começamos por ouvir.'],
|
['icon_key' => 'checklist', 'title' => 'Estruturação do planejamento', 'description' => 'Organização do projeto desde o início, no ritmo do casal.'],
|
||||||
['icon_key' => 'checklist', 'title' => 'Planejamento completo', 'description' => 'Cada etapa mapeada com clareza.'],
|
['icon_key' => 'calendar', 'title' => 'Cronogramas e prioridades', 'description' => 'Prazos e etapas visíveis para decidir com calma.'],
|
||||||
['icon_key' => 'map', 'title' => 'Gestão de fornecedores', 'description' => 'Contatos e prazos sob cuidado.'],
|
['icon_key' => 'map', 'title' => 'Contratação e gestão de fornecedores', 'description' => 'Orientação e acompanhamento de quem entra no projeto.'],
|
||||||
['icon_key' => 'calendar', 'title' => 'Acompanhamento contínuo', 'description' => 'Perto do casal em cada decisão.'],
|
['icon_key' => 'users', 'title' => 'Alinhamento entre envolvidos', 'description' => 'Casal, fornecedores e demais partes na mesma conversa.'],
|
||||||
['icon_key' => 'spark', 'title' => 'Produção e logística', 'description' => 'Dia a dia do evento organizado nos detalhes.'],
|
['icon_key' => 'spark', 'title' => 'Contratos e informações', 'description' => 'Conferência do que foi combinado antes do grande dia.'],
|
||||||
['icon_key' => 'users', 'title' => 'Dia do evento', 'description' => 'Coordenação total para o casal viver o momento.'],
|
['icon_key' => 'heart', 'title' => 'Coordenação da execução', 'description' => 'Acompanhamento do evento para o casal viver o momento.'],
|
||||||
],
|
],
|
||||||
'audience_heading' => 'Para quem é este pacote',
|
'audience_heading' => 'Para quem é esta modalidade',
|
||||||
'audience_intro' => 'Para quem quer uma assessoria completa, do início ao fim.',
|
'audience_intro' => 'Para quem deseja ter ao lado uma profissional conduzindo o processo como um todo, com organização, estratégia e tranquilidade.',
|
||||||
'audience_points' => [
|
'audience_points' => [
|
||||||
'Casamentos',
|
'Casais no início da organização',
|
||||||
'Aniversários',
|
'Quem ainda está estruturando etapas importantes',
|
||||||
'Eventos sociais e celebrações',
|
'Quem quer acompanhamento do planejamento à execução',
|
||||||
'Eventos corporativos',
|
|
||||||
],
|
],
|
||||||
'final_cta_heading' => 'Pronta para começar?',
|
'final_cta_heading' => 'Vamos nos conhecer?',
|
||||||
'final_cta_body' => 'Conte seu evento para a Amare e receba uma proposta.',
|
'final_cta_body' => 'Antes de definir qualquer caminho, queremos conhecer vocês. Basta entrar em contato para uma conversa.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'Conduzione',
|
'name' => 'Conduzione',
|
||||||
'slug' => 'conduzione',
|
'slug' => 'conduzione',
|
||||||
'level' => '02 / PARCIAL',
|
'level' => '02 / PARCIAL',
|
||||||
'tag' => 'Assessoria parcial',
|
'tag' => 'Assessoria parcial',
|
||||||
'subtitle' => 'Para quem já começou e quer seguir acompanhado.',
|
'subtitle' => 'Para quem já começou, mas quer seguir acompanhado.',
|
||||||
'summary' => 'Para casais que já tomaram decisões e contrataram parte dos fornecedores, mas precisam estruturar o que falta.',
|
'summary' => 'Indicada para casais que já iniciaram o planejamento e possuem parte dos fornecedores ou decisões definidas, mas desejam organizar o que falta e conduzir as próximas etapas.',
|
||||||
'scope_items' => [
|
'scope_items' => [
|
||||||
'Diagnóstico do planejamento existente',
|
'Diagnóstico do planejamento já realizado',
|
||||||
'Organização das pendências',
|
'Organização das etapas pendentes',
|
||||||
'Gestão dos fornecedores contratados',
|
'Gestão e acompanhamento dos fornecedores já contratados',
|
||||||
'Orientação para próximas decisões',
|
'Orientação sobre as próximas decisões',
|
||||||
'Coordenação da execução',
|
'Acompanhamento de prazos e informações',
|
||||||
|
'Comunicação e alinhamento com fornecedores',
|
||||||
|
'Organização do cronograma do evento',
|
||||||
|
'Coordenação da execução no grande dia',
|
||||||
],
|
],
|
||||||
'cta_label' => 'Quero conhecer a Conduzione',
|
'cta_label' => 'Quero conhecer a Conduzione',
|
||||||
'compare_heading' => 'Trazer a Amare para o caminho',
|
'compare_heading' => 'Trazer a Amare para o caminho',
|
||||||
'compare_summary' => 'O planejamento existe, mas ainda há decisões e gestão pela frente.',
|
'compare_summary' => 'O planejamento existe; a Amare organiza o que falta e conduz as próximas etapas.',
|
||||||
'sort_order' => 2,
|
'sort_order' => 2,
|
||||||
'eyebrow' => 'Assessoria',
|
'eyebrow' => 'Assessoria',
|
||||||
'title_line' => 'Assessoria',
|
'title_line' => 'Assessoria',
|
||||||
'title_emphasis' => 'Parcial',
|
'title_emphasis' => 'Parcial',
|
||||||
'hero_lead' => 'Para retomar o planejamento, organizar o que falta e seguir com acompanhamento profissional.',
|
'hero_lead' => 'A Amare entra no momento em que vocês estão, compreende o que já foi construído e assume a gestão das etapas necessárias dentro do planejamento.',
|
||||||
'benefits' => [
|
'benefits' => [
|
||||||
['icon_key' => 'checklist', 'label' => 'Diagnóstico completo'],
|
['icon_key' => 'checklist', 'label' => 'Diagnóstico do que já existe'],
|
||||||
['icon_key' => 'calendar', 'label' => 'Organização integral'],
|
['icon_key' => 'calendar', 'label' => 'Pendências organizadas'],
|
||||||
['icon_key' => 'users', 'label' => 'Fornecedores selecionados'],
|
['icon_key' => 'users', 'label' => 'Fornecedores acompanhados'],
|
||||||
['icon_key' => 'heart', 'label' => 'Experiência leve e inesquecível'],
|
['icon_key' => 'heart', 'label' => 'Próximas decisões com clareza'],
|
||||||
],
|
],
|
||||||
'included_items' => [
|
'included_items' => [
|
||||||
['icon_key' => 'checklist', 'title' => 'Diagnóstico do planejamento', 'description' => 'Um olhar profissional sobre o que já existe.'],
|
['icon_key' => 'checklist', 'title' => 'Diagnóstico do planejamento', 'description' => 'Um olhar profissional sobre o que o casal já construiu.'],
|
||||||
['icon_key' => 'calendar', 'title' => 'Organização das pendências', 'description' => 'Prioridades claras, etapa a etapa.'],
|
['icon_key' => 'calendar', 'title' => 'Etapas pendentes', 'description' => 'O que falta ganha ordem, prazo e prioridade.'],
|
||||||
['icon_key' => 'map', 'title' => 'Gestão de fornecedores', 'description' => 'Contatos e prazos sob cuidado.'],
|
['icon_key' => 'map', 'title' => 'Fornecedores já contratados', 'description' => 'Gestão e comunicação com quem já está no projeto.'],
|
||||||
['icon_key' => 'spark', 'title' => 'Gestão dos próximos passos', 'description' => 'Um plano para chegar tranquilo ao grande dia.'],
|
['icon_key' => 'spark', 'title' => 'Próximas decisões', 'description' => 'Orientação para seguir sem retrabalho.'],
|
||||||
['icon_key' => 'users', 'title' => 'Acompanhamento contínuo', 'description' => 'Perto do casal em cada decisão.'],
|
['icon_key' => 'users', 'title' => 'Cronograma do evento', 'description' => 'A operação do grande dia começa a ficar visível.'],
|
||||||
['icon_key' => 'heart', 'title' => 'Dia do evento', 'description' => 'Coordenação total para o casal viver o momento.'],
|
['icon_key' => 'heart', 'title' => 'Coordenação no grande dia', 'description' => 'Execução acompanhada para o casal viver o momento.'],
|
||||||
],
|
],
|
||||||
'audience_heading' => 'Para quem é este pacote',
|
'audience_heading' => 'Para quem é esta modalidade',
|
||||||
'audience_intro' => 'Para quem já começou a planejar e quer retomar as rédeas com acompanhamento profissional.',
|
'audience_intro' => 'Para quem já deu os primeiros passos, mas deseja seguir com mais segurança, organização e tranquilidade.',
|
||||||
'audience_points' => [
|
'audience_points' => [
|
||||||
'Casamentos em andamento',
|
'Casais que já iniciaram o planejamento',
|
||||||
'Casais com pouco tempo disponível',
|
'Quem já tem parte dos fornecedores ou decisões definidas',
|
||||||
'Quem precisa priorizar pendências',
|
'Quem quer condução profissional nas etapas que faltam',
|
||||||
'Quem busca fornecedores confiáveis',
|
|
||||||
],
|
],
|
||||||
'final_cta_heading' => 'Pronta para retomar?',
|
'final_cta_heading' => 'Vamos organizar o que falta?',
|
||||||
'final_cta_body' => 'Conte onde seu planejamento está e receba uma proposta.',
|
'final_cta_body' => 'Conte onde o planejamento está. A Amare entra nesse momento e conduz as próximas etapas.',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'Grand Jour',
|
'name' => 'Grand Jour',
|
||||||
'slug' => 'grand-jour',
|
'slug' => 'grand-jour',
|
||||||
'level' => '03 / FINAL',
|
'level' => '03 / FINAL',
|
||||||
'tag' => 'Assessoria final',
|
'tag' => 'Assessoria final',
|
||||||
'subtitle' => 'O planejamento está pronto. Agora é hora de viver.',
|
'subtitle' => 'Para quem já planejou e precisa de alguém para assumir a operação.',
|
||||||
'summary' => 'Para casais na reta final que precisam de uma equipe para assumir alinhamentos, cronograma e operação.',
|
'summary' => 'Indicada para casais que estão na reta final dos preparativos e já possuem a maior parte do casamento definida.',
|
||||||
'scope_items' => [
|
'scope_items' => [
|
||||||
'Imersão no planejamento existente',
|
'Imersão no planejamento existente',
|
||||||
'Conferência das informações',
|
'Conferência das informações e contratos dos fornecedores',
|
||||||
'Alinhamento dos fornecedores',
|
'Alinhamento e comunicação com os profissionais envolvidos',
|
||||||
'Organização do cronograma final',
|
'Organização do cronograma final',
|
||||||
'Gestão da operação do evento',
|
'Orientação sobre os últimos detalhes',
|
||||||
|
'Coordenação dos fornecedores no evento',
|
||||||
|
'Acompanhamento da montagem e dos momentos do casamento',
|
||||||
|
'Gestão da operação durante o grande dia',
|
||||||
],
|
],
|
||||||
'cta_label' => 'Quero conhecer a Grand Jour',
|
'cta_label' => 'Quero conhecer a Grand Jour',
|
||||||
'compare_heading' => 'Entregar a operação para a Amare',
|
'compare_heading' => 'Entregar a operação para a Amare',
|
||||||
'compare_summary' => 'O projeto está pronto e o foco passa a ser alinhamento e execução.',
|
'compare_summary' => 'O projeto está pronto; o foco passa a ser alinhamento e execução.',
|
||||||
'sort_order' => 3,
|
'sort_order' => 3,
|
||||||
'eyebrow' => 'Assessoria',
|
'eyebrow' => 'Assessoria',
|
||||||
'title_line' => 'Assessoria',
|
'title_line' => 'Assessoria',
|
||||||
'title_emphasis' => 'Final',
|
'title_emphasis' => 'Final',
|
||||||
'hero_lead' => 'Para assumir os alinhamentos e a operação quando o grande dia está próximo.',
|
'hero_lead' => 'A Amare assume a condução operacional, mergulha no planejamento construído pelo casal e transforma as informações em uma execução organizada e alinhada.',
|
||||||
'benefits' => [
|
'benefits' => [
|
||||||
['icon_key' => 'checklist', 'label' => 'Imersão no que já existe'],
|
['icon_key' => 'checklist', 'label' => 'Imersão no planejamento'],
|
||||||
['icon_key' => 'users', 'label' => 'Alinhamento de fornecedores'],
|
['icon_key' => 'users', 'label' => 'Fornecedores alinhados'],
|
||||||
['icon_key' => 'calendar', 'label' => 'Cronograma final'],
|
['icon_key' => 'calendar', 'label' => 'Cronograma final'],
|
||||||
['icon_key' => 'heart', 'label' => 'Operação com leveza'],
|
['icon_key' => 'heart', 'label' => 'Operação no grande dia'],
|
||||||
],
|
],
|
||||||
'included_items' => [
|
'included_items' => [
|
||||||
['icon_key' => 'checklist', 'title' => 'Imersão no planejamento', 'description' => 'Entendemos cada decisão já tomada.'],
|
['icon_key' => 'checklist', 'title' => 'Imersão no planejamento', 'description' => 'A Amare mergulha no que o casal já decidiu.'],
|
||||||
['icon_key' => 'map', 'title' => 'Alinhamento de fornecedores', 'description' => 'Contatos e prazos sob cuidado.'],
|
['icon_key' => 'map', 'title' => 'Contratos e informações', 'description' => 'Conferência do combinado com cada profissional.'],
|
||||||
['icon_key' => 'calendar', 'title' => 'Cronograma final', 'description' => 'Cada etapa posicionada na reta final.'],
|
['icon_key' => 'users', 'title' => 'Alinhamento dos envolvidos', 'description' => 'Comunicação centralizada na reta final.'],
|
||||||
['icon_key' => 'spark', 'title' => 'Ensaios e detalhes', 'description' => 'Produção e logística nos detalhes.'],
|
['icon_key' => 'calendar', 'title' => 'Cronograma final', 'description' => 'A operação do grande dia ganha ordem.'],
|
||||||
['icon_key' => 'users', 'title' => 'Operação do evento', 'description' => 'Gestão da equipe e do dia a dia.'],
|
['icon_key' => 'spark', 'title' => 'Últimos detalhes', 'description' => 'Orientação para fechar o que ainda falta.'],
|
||||||
['icon_key' => 'heart', 'title' => 'Dia do evento', 'description' => 'Coordenação total para o casal viver o momento.'],
|
['icon_key' => 'heart', 'title' => 'Gestão da operação', 'description' => 'Montagem, momentos e condução no dia do casamento.'],
|
||||||
],
|
],
|
||||||
'audience_heading' => 'Para quem é este pacote',
|
'audience_heading' => 'Para quem é esta modalidade',
|
||||||
'audience_intro' => 'Para casais já planejados que querem chegar ao grande dia com equipe e operação no lugar.',
|
'audience_intro' => 'Porque, na reta final, vocês não precisam de mais tarefas. Precisam de alguém que assuma a condução.',
|
||||||
'audience_points' => [
|
'audience_points' => [
|
||||||
'Casamentos já planejados',
|
'Casais na reta final dos preparativos',
|
||||||
'Casais na reta final',
|
'Quem já definiu a maior parte do casamento',
|
||||||
'Quem precisa de operação no dia',
|
'Quem quer entregar a operação do grande dia',
|
||||||
'Quem quer viver o momento com leveza',
|
|
||||||
],
|
],
|
||||||
'final_cta_heading' => 'Pronta para o grande dia?',
|
'final_cta_heading' => 'Vamos assumir a operação?',
|
||||||
'final_cta_body' => 'Conte a data do seu evento e receba uma proposta.',
|
'final_cta_body' => 'Conte a data e o que já está definido. A Amare entra para conduzir o grande dia.',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,51 +1,28 @@
|
|||||||
# Domain Docs
|
# Domain Docs
|
||||||
|
|
||||||
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
|
How engineering skills should consume this repo's domain documentation.
|
||||||
|
|
||||||
## Before exploring, read these
|
## Single source of truth
|
||||||
|
|
||||||
- **`CONTEXT.md`** at the repo root, or
|
**`SPEC.md` §8** (especially **§8.0 Linguagem ubíqua**) is the glossary and domain model for Amare. There is no root `CONTEXT.md` on purpose — do not create one. Prefer `SPEC.md` over Linear copy or OpenSpec prose when terms conflict.
|
||||||
- **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
|
||||||
- **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
|
||||||
|
|
||||||
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
|
Also useful:
|
||||||
|
|
||||||
## File structure
|
- **`PRODUCT.md`** — positioning and audience (not the glossary)
|
||||||
|
- **`openspec/specs/`** — current capability contracts
|
||||||
|
- **`docs/adr/README.md`** — index only; ADR-001–010 live in `SPEC.md` §21
|
||||||
|
|
||||||
Single-context repo (most repos):
|
## Before exploring
|
||||||
|
|
||||||
```
|
1. Read `SPEC.md` §8.0 for vocabulary; skim §5.1 for public routes and §8.2+ for persistence shape when relevant.
|
||||||
/
|
2. If a skill expects `CONTEXT.md` / `CONTEXT-MAP.md` and finds neither, **proceed using `SPEC.md` §8** — do not invent a parallel glossary file.
|
||||||
├── CONTEXT.md
|
|
||||||
├── docs/adr/
|
|
||||||
│ ├── 0001-event-sourced-orders.md
|
|
||||||
│ └── 0002-postgres-for-write-model.md
|
|
||||||
└── src/
|
|
||||||
```
|
|
||||||
|
|
||||||
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
|
||||||
|
|
||||||
```
|
|
||||||
/
|
|
||||||
├── CONTEXT-MAP.md
|
|
||||||
├── docs/adr/ ← system-wide decisions
|
|
||||||
└── src/
|
|
||||||
├── ordering/
|
|
||||||
│ ├── CONTEXT.md
|
|
||||||
│ └── docs/adr/ ← context-specific decisions
|
|
||||||
└── billing/
|
|
||||||
├── CONTEXT.md
|
|
||||||
└── docs/adr/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use the glossary's vocabulary
|
## Use the glossary's vocabulary
|
||||||
|
|
||||||
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
When output names a domain concept (issue title, refactor, hypothesis, test name), use the term as defined in `SPEC.md` §8.0. Don't drift to synonyms the glossary explicitly avoids (`_Avoid_`).
|
||||||
|
|
||||||
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
|
If the concept isn't in §8.0 yet, either you're inventing language the project doesn't use (reconsider) or there's a real gap — resolve it by updating `SPEC.md` §8.0 via `/grill-with-docs` / domain-modeling, not by adding `CONTEXT.md`.
|
||||||
|
|
||||||
## Flag ADR conflicts
|
## Flag ADR conflicts
|
||||||
|
|
||||||
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
If output contradicts an ADR in `SPEC.md` §21, surface it explicitly rather than silently overriding.
|
||||||
|
|
||||||
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
|
|
||||||
|
|||||||
11
package-lock.json
generated
11
package-lock.json
generated
@@ -5,7 +5,6 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fontsource/eb-garamond": "^5.2.7",
|
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"daisyui": "^5.7.16",
|
"daisyui": "^5.7.16",
|
||||||
@@ -50,16 +49,6 @@
|
|||||||
"tslib": "^2.4.0"
|
"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": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.13",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fontsource/eb-garamond": "^5.2.7",
|
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"daisyui": "^5.7.16",
|
"daisyui": "^5.7.16",
|
||||||
|
|||||||
@@ -189,18 +189,6 @@
|
|||||||
overflow-x: clip;
|
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) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
:where(a, button, input, textarea, select) {
|
:where(a, button, input, textarea, select) {
|
||||||
transition-duration: var(--amare-motion-feedback);
|
transition-duration: var(--amare-motion-feedback);
|
||||||
@@ -212,45 +200,6 @@
|
|||||||
transform: translateY(1px);
|
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). */
|
/* 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] {
|
html[data-motion="enhance"] [data-motion="page-open"]:not(.is-active) [data-motion-beat] {
|
||||||
transform: translateY(var(--amare-motion-distance-y));
|
transform: translateY(var(--amare-motion-distance-y));
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<div class="lg:col-span-6 lg:col-start-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>
|
<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">
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
autocomplete="name"
|
autocomplete="name"
|
||||||
maxlength="120"
|
maxlength="120"
|
||||||
placeholder="Seu nome"
|
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') aria-invalid="true" aria-describedby="briefing-nome-error" @enderror
|
||||||
>
|
>
|
||||||
@error('nome')
|
@error('nome')
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
maxlength="254"
|
maxlength="254"
|
||||||
placeholder="voce@email.com"
|
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') aria-invalid="true" aria-describedby="briefing-email-error" @enderror
|
||||||
>
|
>
|
||||||
@error('email')
|
@error('email')
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
autocomplete="tel"
|
autocomplete="tel"
|
||||||
maxlength="40"
|
maxlength="40"
|
||||||
placeholder="(11) 90000-0000"
|
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') aria-invalid="true" aria-describedby="briefing-telefone-error" @enderror
|
||||||
>
|
>
|
||||||
@error('telefone')
|
@error('telefone')
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
id="briefing-tipo_evento"
|
id="briefing-tipo_evento"
|
||||||
name="tipo_evento"
|
name="tipo_evento"
|
||||||
required
|
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
|
@error('tipo_evento') aria-invalid="true" aria-describedby="briefing-tipo_evento-error" @enderror
|
||||||
>
|
>
|
||||||
<option value="" selected disabled>Selecione...</option>
|
<option value="" selected disabled>Selecione...</option>
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
value="{{ old('data_periodo') }}"
|
value="{{ old('data_periodo') }}"
|
||||||
maxlength="80"
|
maxlength="80"
|
||||||
placeholder="Ex.: novembro de 2027"
|
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') aria-invalid="true" aria-describedby="briefing-data_periodo-error" @enderror
|
||||||
>
|
>
|
||||||
@error('data_periodo')
|
@error('data_periodo')
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
required
|
required
|
||||||
maxlength="80"
|
maxlength="80"
|
||||||
placeholder="Ex.: São Paulo"
|
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') aria-invalid="true" aria-describedby="briefing-cidade-error" @enderror
|
||||||
>
|
>
|
||||||
@error('cidade')
|
@error('cidade')
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="Ex.: 120"
|
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') aria-invalid="true" aria-describedby="briefing-convidados-error" @enderror
|
||||||
>
|
>
|
||||||
@error('convidados')
|
@error('convidados')
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
value="{{ old('servico_interesse') }}"
|
value="{{ old('servico_interesse') }}"
|
||||||
maxlength="120"
|
maxlength="120"
|
||||||
placeholder="Ex.: planejamento completo"
|
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') aria-invalid="true" aria-describedby="briefing-servico_interesse-error" @enderror
|
||||||
>
|
>
|
||||||
@error('servico_interesse')
|
@error('servico_interesse')
|
||||||
@@ -198,7 +198,7 @@
|
|||||||
rows="5"
|
rows="5"
|
||||||
maxlength="3000"
|
maxlength="3000"
|
||||||
placeholder="O que já está definido? Em que ponto você precisa de ajuda?"
|
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
|
@error('mensagem') aria-invalid="true" aria-describedby="briefing-mensagem-error" @enderror
|
||||||
>{{ old('mensagem') }}</textarea>
|
>{{ old('mensagem') }}</textarea>
|
||||||
@error('mensagem')
|
@error('mensagem')
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
@if (filled($settings->hero_secondary_cta_label))
|
@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">
|
<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>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,9 +14,6 @@
|
|||||||
'note' => 'Nomenclaturas exibidas conforme materiais/reunião; confirmar versão final antes da publicação.',
|
'note' => 'Nomenclaturas exibidas conforme materiais/reunião; confirmar versão final antes da publicação.',
|
||||||
])
|
])
|
||||||
|
|
||||||
@php
|
|
||||||
$bandHref = $bandCtaHref ?? route('briefing');
|
|
||||||
@endphp
|
|
||||||
<section
|
<section
|
||||||
aria-labelledby="packages-heading"
|
aria-labelledby="packages-heading"
|
||||||
class="home-chapter border-b border-amare-border bg-amare-bg"
|
class="home-chapter border-b border-amare-border bg-amare-bg"
|
||||||
@@ -64,7 +61,7 @@
|
|||||||
href="{{ route('packages.show', $package->slug) }}"
|
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"
|
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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -72,12 +69,20 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@php($orientationLink = \App\Support\PackageContactLink::forOrientation($settings))
|
||||||
|
@php($bandHref = filled($bandCtaHref) ? $bandCtaHref : $orientationLink['href'])
|
||||||
|
@php($bandIsWhatsapp = ! filled($bandCtaHref) && $orientationLink['isWhatsapp'])
|
||||||
<div class="mt-6 flex flex-col justify-between gap-8 bg-amare-accent-deep p-8 text-amare-accent-text md:flex-row md:items-center md:p-12" data-reveal data-reveal-from="up">
|
<div class="mt-6 flex flex-col justify-between gap-8 bg-amare-accent-deep p-8 text-amare-accent-text md:flex-row md:items-center md:p-12" data-reveal data-reveal-from="up">
|
||||||
<div class="max-w-xl space-y-3">
|
<div class="max-w-xl space-y-3">
|
||||||
<h3 class="text-[clamp(1.5625rem,2.7vw,2.125rem)] font-medium leading-tight">Ainda não sabe qual modalidade é ideal?</h3>
|
<h3 class="text-[clamp(1.5625rem,2.7vw,2.125rem)] font-medium leading-tight">Ainda não sabe qual modalidade é ideal?</h3>
|
||||||
<p class="text-amare-accent-text/80">{{ $bandBody }}</p>
|
<p class="text-amare-accent-text/80">{{ $bandBody }}</p>
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ $bandHref }}" class="inline-flex min-h-[48px] items-center justify-center self-start border border-amare-accent-text px-6 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent-text transition-colors hover:bg-amare-accent-text hover:text-amare-accent-deep md:self-auto">
|
<a
|
||||||
|
data-testid="package-guidance-cta"
|
||||||
|
href="{{ $bandHref }}"
|
||||||
|
@if ($bandIsWhatsapp) target="_blank" rel="noopener noreferrer" @endif
|
||||||
|
class="inline-flex min-h-[48px] items-center justify-center self-start border border-amare-accent-text px-6 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent-text transition-colors hover:bg-amare-accent-text hover:text-amare-accent-deep md:self-auto"
|
||||||
|
>
|
||||||
Conversar com a Amare
|
Conversar com a Amare
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<p>
|
<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">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<p>
|
<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">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<ol class="grid gap-8 border-t border-amare-border pt-6 md:grid-cols-3">
|
<ol class="grid gap-8 border-t border-amare-border pt-6 md:grid-cols-3">
|
||||||
@foreach ($packages as $package)
|
@foreach ($packages as $package)
|
||||||
@php($contactLink = \App\Support\PackageContactLink::for($settings, $package->name))
|
@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
|
@endforeach
|
||||||
</ol>
|
</ol>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$pageMeta = new \App\Application\Data\PageMeta(
|
$pageMeta = new \App\Application\Data\PageMeta(
|
||||||
title: 'Página não encontrada - Amare Assessoria',
|
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: '',
|
canonical: '',
|
||||||
robots: 'noindex, nofollow',
|
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>
|
<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>
|
<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>
|
<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">
|
<p data-reveal data-reveal-from="up">
|
||||||
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
||||||
Voltar para a home
|
Voltar para a home
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$pageMeta = new \App\Application\Data\PageMeta(
|
$pageMeta = new \App\Application\Data\PageMeta(
|
||||||
title: 'Sessão expirada - Amare Assessoria',
|
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: '',
|
canonical: '',
|
||||||
robots: 'noindex, nofollow',
|
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>
|
<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>
|
<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>
|
<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">
|
<p data-reveal data-reveal-from="up">
|
||||||
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
||||||
Voltar para a home
|
Voltar para a home
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$pageMeta = new \App\Application\Data\PageMeta(
|
$pageMeta = new \App\Application\Data\PageMeta(
|
||||||
title: 'Muitas solicitações - Amare Assessoria',
|
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: '',
|
canonical: '',
|
||||||
robots: 'noindex, nofollow',
|
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>
|
<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>
|
<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>
|
<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">
|
<p data-reveal data-reveal-from="up">
|
||||||
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
||||||
Voltar para a home
|
Voltar para a home
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$pageMeta = new \App\Application\Data\PageMeta(
|
$pageMeta = new \App\Application\Data\PageMeta(
|
||||||
title: 'Algo deu errado - Amare Assessoria',
|
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: '',
|
canonical: '',
|
||||||
robots: 'noindex, nofollow',
|
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>
|
<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>
|
<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>
|
<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. Já estamos cuidando disso — tente novamente em instantes.</p>
|
<p class="text-amare-muted" data-reveal data-reveal-from="up">Não foi possível concluir o pedido agora. Tente novamente em instantes.</p>
|
||||||
<p data-reveal data-reveal-from="up">
|
<p data-reveal data-reveal-from="up">
|
||||||
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
||||||
Voltar para a home
|
Voltar para a home
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$pageMeta = new \App\Application\Data\PageMeta(
|
$pageMeta = new \App\Application\Data\PageMeta(
|
||||||
title: 'Em manutenção - Amare Assessoria',
|
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: '',
|
canonical: '',
|
||||||
robots: 'noindex, nofollow',
|
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>
|
<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>
|
<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>
|
<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">
|
<p data-reveal data-reveal-from="up">
|
||||||
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
<a href="{{ route('home') }}" class="btn btn-primary text-sm font-semibold uppercase tracking-[0.12em]">
|
||||||
Voltar para a home
|
Voltar para a home
|
||||||
|
|||||||
@@ -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">
|
<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">
|
<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>
|
<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('/#casamentos') }}" 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">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="{{ url('/#corporate') }}" 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">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('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="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('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>
|
<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>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,10 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
@if (session('status') === 'briefing-sent')
|
@if (session('status') === 'briefing-sent')
|
||||||
<x-public.form-success
|
<div role="status" class="alert mt-8 border border-amare-border bg-amare-bg-deep">
|
||||||
seal="Briefing recebido"
|
<p class="font-semibold text-amare-text">Mensagem enviada.</p>
|
||||||
title="Recebemos sua ficha."
|
<p class="mt-1 text-amare-muted">Recebemos seu briefing e retornaremos em breve pelo canal informado.</p>
|
||||||
body="Vamos ler com atenção e retornar em breve pelo canal que você informou. Enquanto isso, pode seguir explorando o portfólio."
|
</div>
|
||||||
/>
|
|
||||||
@else
|
@else
|
||||||
<form
|
<form
|
||||||
method="POST"
|
method="POST"
|
||||||
@@ -80,7 +79,7 @@
|
|||||||
autocomplete="name"
|
autocomplete="name"
|
||||||
maxlength="120"
|
maxlength="120"
|
||||||
placeholder="Seu nome"
|
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') aria-invalid="true" aria-describedby="nome-error" @enderror
|
||||||
>
|
>
|
||||||
@error('nome')
|
@error('nome')
|
||||||
@@ -99,7 +98,7 @@
|
|||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
maxlength="254"
|
maxlength="254"
|
||||||
placeholder="voce@email.com"
|
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') aria-invalid="true" aria-describedby="email-error" @enderror
|
||||||
>
|
>
|
||||||
@error('email')
|
@error('email')
|
||||||
@@ -118,7 +117,7 @@
|
|||||||
autocomplete="tel"
|
autocomplete="tel"
|
||||||
maxlength="40"
|
maxlength="40"
|
||||||
placeholder="(11) 90000-0000"
|
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') aria-invalid="true" aria-describedby="telefone-error" @enderror
|
||||||
>
|
>
|
||||||
@error('telefone')
|
@error('telefone')
|
||||||
@@ -132,7 +131,7 @@
|
|||||||
id="tipo_evento"
|
id="tipo_evento"
|
||||||
name="tipo_evento"
|
name="tipo_evento"
|
||||||
required
|
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
|
@error('tipo_evento') aria-invalid="true" aria-describedby="tipo_evento-error" @enderror
|
||||||
>
|
>
|
||||||
<option value="" selected disabled>Selecione...</option>
|
<option value="" selected disabled>Selecione...</option>
|
||||||
@@ -155,7 +154,7 @@
|
|||||||
value="{{ old('data_periodo') }}"
|
value="{{ old('data_periodo') }}"
|
||||||
maxlength="80"
|
maxlength="80"
|
||||||
placeholder="Ex.: novembro de 2027"
|
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') aria-invalid="true" aria-describedby="data_periodo-error" @enderror
|
||||||
>
|
>
|
||||||
@error('data_periodo')
|
@error('data_periodo')
|
||||||
@@ -173,7 +172,7 @@
|
|||||||
required
|
required
|
||||||
maxlength="80"
|
maxlength="80"
|
||||||
placeholder="Ex.: São Paulo"
|
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') aria-invalid="true" aria-describedby="cidade-error" @enderror
|
||||||
>
|
>
|
||||||
@error('cidade')
|
@error('cidade')
|
||||||
@@ -193,7 +192,7 @@
|
|||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="Ex.: 120"
|
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') aria-invalid="true" aria-describedby="convidados-error" @enderror
|
||||||
>
|
>
|
||||||
@error('convidados')
|
@error('convidados')
|
||||||
@@ -210,7 +209,7 @@
|
|||||||
value="{{ old('servico_interesse', request('servico_interesse')) }}"
|
value="{{ old('servico_interesse', request('servico_interesse')) }}"
|
||||||
maxlength="120"
|
maxlength="120"
|
||||||
placeholder="Ex.: planejamento completo"
|
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') aria-invalid="true" aria-describedby="servico_interesse-error" @enderror
|
||||||
>
|
>
|
||||||
@error('servico_interesse')
|
@error('servico_interesse')
|
||||||
@@ -228,7 +227,7 @@
|
|||||||
rows="6"
|
rows="6"
|
||||||
maxlength="3000"
|
maxlength="3000"
|
||||||
placeholder="Conte sobre o seu evento, expectativas e principais preocupações."
|
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
|
@error('mensagem') aria-invalid="true" aria-describedby="mensagem-error" @enderror
|
||||||
>{{ old('mensagem') }}</textarea>
|
>{{ old('mensagem') }}</textarea>
|
||||||
@error('mensagem')
|
@error('mensagem')
|
||||||
|
|||||||
@@ -20,12 +20,7 @@
|
|||||||
<div class="container-amare py-16 md:py-24" data-reveal-group>
|
<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>
|
<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')
|
@if (session('status') === 'partner-inquiry-sent')
|
||||||
<x-public.form-success
|
<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>
|
||||||
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."
|
|
||||||
/>
|
|
||||||
@else
|
@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">
|
<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
|
@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
|
@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">
|
<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)
|
@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
|
@endforeach
|
||||||
</div>
|
</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>
|
<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>
|
<button type="submit" data-submit-button class="btn btn-primary text-sm font-semibold uppercase tracking-[.12em]">Enviar apresentação</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
Conhecer os serviços
|
Conhecer os serviços
|
||||||
</a>
|
</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">
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</x-public.photo-hero>
|
</x-public.photo-hero>
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
:show-intro="false"
|
:show-intro="false"
|
||||||
kicker="tag"
|
kicker="tag"
|
||||||
:show-subtitle="true"
|
:show-subtitle="true"
|
||||||
:band-cta-href="route('briefing')"
|
|
||||||
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."
|
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"
|
:show-note="false"
|
||||||
/>
|
/>
|
||||||
@@ -65,7 +64,7 @@
|
|||||||
<p class="text-amare-text-muted">{{ $package->compare_summary }}</p>
|
<p class="text-amare-text-muted">{{ $package->compare_summary }}</p>
|
||||||
</div>
|
</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">
|
<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>
|
</a>
|
||||||
</article>
|
</article>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -38,6 +38,33 @@ class WeddingPackageTest extends TestCase
|
|||||||
['Essenza', 'Conduzione', 'Grand Jour'],
|
['Essenza', 'Conduzione', 'Grand Jour'],
|
||||||
WeddingPackage::query()->published()->orderBy('sort_order')->pluck('name')->all(),
|
WeddingPackage::query()->published()->orderBy('sort_order')->pluck('name')->all(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$essenza = WeddingPackage::query()->where('name', 'Essenza')->first();
|
||||||
|
$this->assertNotNull($essenza);
|
||||||
|
$this->assertSame('Assessoria completa', $essenza->tag);
|
||||||
|
$this->assertSame('Do primeiro planejamento ao grande dia.', $essenza->subtitle);
|
||||||
|
$this->assertStringContainsString('desde o início da organização', (string) $essenza->summary);
|
||||||
|
$this->assertContains('Estruturação e acompanhamento do planejamento', $essenza->scope_items);
|
||||||
|
$this->assertSame(
|
||||||
|
['Casais no início da organização', 'Quem ainda está estruturando etapas importantes', 'Quem quer acompanhamento do planejamento à execução'],
|
||||||
|
$essenza->audience_points,
|
||||||
|
);
|
||||||
|
|
||||||
|
$publicCopy = WeddingPackage::query()->published()->get()->map(fn (WeddingPackage $package): string => json_encode([
|
||||||
|
$package->summary,
|
||||||
|
$package->subtitle,
|
||||||
|
$package->hero_lead,
|
||||||
|
$package->scope_items,
|
||||||
|
$package->included_items,
|
||||||
|
$package->audience_intro,
|
||||||
|
$package->final_cta_body,
|
||||||
|
], JSON_THROW_ON_ERROR))->implode(' ');
|
||||||
|
|
||||||
|
$this->assertStringNotContainsString('R$', $publicCopy);
|
||||||
|
$this->assertStringNotContainsString('3.500', $publicCopy);
|
||||||
|
$this->assertStringNotContainsString('Assessoria VIP', $publicCopy);
|
||||||
|
$this->assertStringNotContainsString('90 km', $publicCopy);
|
||||||
|
$this->assertStringNotContainsString('3,49%', $publicCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_factory_generates_a_slug_and_autofills_blank_slug_from_name(): void
|
public function test_factory_generates_a_slug_and_autofills_blank_slug_from_name(): void
|
||||||
|
|||||||
@@ -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
|
public function test_submission_normalizes_email_and_phone_before_reaching_mailables(): void
|
||||||
{
|
{
|
||||||
$settings = SiteSetting::instance();
|
$settings = SiteSetting::instance();
|
||||||
|
|||||||
@@ -72,19 +72,7 @@ class HeritageEditorialTokensTest extends TestCase
|
|||||||
{
|
{
|
||||||
$vite = (string) file_get_contents(base_path('vite.config.js'));
|
$vite = (string) file_get_contents(base_path('vite.config.js'));
|
||||||
|
|
||||||
$this->assertMatchesRegularExpression(
|
$this->assertStringContainsString("bunny('EB Garamond'", $vite);
|
||||||
"/^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->assertStringNotContainsString('Instrument Sans', $vite);
|
$this->assertStringNotContainsString('Instrument Sans', $vite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use App\Models\PortfolioCase;
|
|||||||
use App\Models\SiteSetting;
|
use App\Models\SiteSetting;
|
||||||
use App\Models\Testimonial;
|
use App\Models\Testimonial;
|
||||||
use App\Models\WeddingPackage;
|
use App\Models\WeddingPackage;
|
||||||
|
use App\Support\PackageContactLink;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@@ -213,7 +214,24 @@ class HomePageContentTest extends TestCase
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
->assertSee('target="_blank"', false)
|
->assertSee('target="_blank"', false)
|
||||||
->assertDontSee(route('briefing', ['servico_interesse' => 'Grand Jour']), false);
|
->assertDontSee(route('briefing', ['servico_interesse' => 'Grand Jour']), false)
|
||||||
|
->assertSee('data-testid="package-guidance-cta"', false)
|
||||||
|
->assertSee(
|
||||||
|
'https://wa.me/5511988887777?text='.rawurlencode(PackageContactLink::ORIENTATION_MESSAGE),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_orientation_cta_falls_back_to_briefing_without_whatsapp_number(): void
|
||||||
|
{
|
||||||
|
SiteSetting::instance()->update(['whatsapp_number' => null]);
|
||||||
|
|
||||||
|
$response = $this->get(route('home'));
|
||||||
|
|
||||||
|
$response
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('href="'.route('briefing').'"', false)
|
||||||
|
->assertDontSee(PackageContactLink::ORIENTATION_MESSAGE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_corporate_steps_and_placeholder_render_from_settings(): void
|
public function test_corporate_steps_and_placeholder_render_from_settings(): void
|
||||||
|
|||||||
@@ -96,9 +96,6 @@ class MotionMarkupTest extends TestCase
|
|||||||
$this->assertStringContainsString('[data-reveal]', $appCss);
|
$this->assertStringContainsString('[data-reveal]', $appCss);
|
||||||
$this->assertStringNotContainsString('[data-chapter-index]', $appCss);
|
$this->assertStringNotContainsString('[data-chapter-index]', $appCss);
|
||||||
$this->assertStringContainsString('prefers-reduced-motion: no-preference', $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).
|
// Entrance motion must not fade text opacity — mid-fade fails WCAG contrast (axe).
|
||||||
$this->assertDoesNotMatchRegularExpression(
|
$this->assertDoesNotMatchRegularExpression(
|
||||||
|
|||||||
@@ -66,22 +66,6 @@ class PartnerInquiryTest extends TestCase
|
|||||||
Mail::assertQueued(PartnerInquiry::class, 1);
|
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
|
public function test_partner_honeypot_reports_success_without_sending_mail(): void
|
||||||
{
|
{
|
||||||
SiteSetting::instance();
|
SiteSetting::instance();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace Tests\Feature\PublicSite;
|
|||||||
|
|
||||||
use App\Models\SiteSetting;
|
use App\Models\SiteSetting;
|
||||||
use App\Models\WeddingPackage;
|
use App\Models\WeddingPackage;
|
||||||
|
use App\Support\PackageContactLink;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@@ -31,7 +32,12 @@ class ServicesPageCtaTest extends TestCase
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
->assertSee('target="_blank"', false)
|
->assertSee('target="_blank"', false)
|
||||||
->assertDontSee(route('briefing', ['servico_interesse' => 'Grand Jour']), false);
|
->assertDontSee(route('briefing', ['servico_interesse' => 'Grand Jour']), false)
|
||||||
|
->assertSee('data-testid="package-guidance-cta"', false)
|
||||||
|
->assertSee(
|
||||||
|
'https://wa.me/5511988887777?text='.rawurlencode(PackageContactLink::ORIENTATION_MESSAGE),
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_package_cta_falls_back_to_briefing_without_whatsapp_number(): void
|
public function test_package_cta_falls_back_to_briefing_without_whatsapp_number(): void
|
||||||
@@ -50,4 +56,18 @@ class ServicesPageCtaTest extends TestCase
|
|||||||
->assertSee('href="'.route('briefing', ['servico_interesse' => 'Essenza']).'"', false)
|
->assertSee('href="'.route('briefing', ['servico_interesse' => 'Essenza']).'"', false)
|
||||||
->assertDontSee('wa.me/', false);
|
->assertDontSee('wa.me/', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_orientation_cta_uses_distinct_whatsapp_message_when_a_number_is_configured(): void
|
||||||
|
{
|
||||||
|
SiteSetting::instance()->update(['whatsapp_number' => '+55 11 98888-7777']);
|
||||||
|
|
||||||
|
$response = $this->get(route('services.index'));
|
||||||
|
|
||||||
|
$response
|
||||||
|
->assertOk()
|
||||||
|
->assertSee(
|
||||||
|
'https://wa.me/5511988887777?text='.rawurlencode(PackageContactLink::ORIENTATION_MESSAGE),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import laravel from 'laravel-vite-plugin';
|
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';
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,7 +76,7 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
fonts: [
|
fonts: [
|
||||||
fontsource('EB Garamond', {
|
bunny('EB Garamond', {
|
||||||
weights: [400, 500, 600],
|
weights: [400, 500, 600],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user