# va-themes/florial

Florial — flower-subscription product theme. Covers the three Florial client
environments (`savitarna`, `pardavimai`, `operacijos`) and is switch-ready for
the public `web` env (Protera stays the web default until Phase 6).

- **Namespace:** `ViartasThemes\Florial\`
- **Provider:** `FlorialThemeServiceProvider` (extends `BaseThemeServiceProvider`)
- **Brand tokens:** `Resources/assets/css/variables.css` (CSS custom properties only)
- **Fonts:** Playfair Display, self-hosted variable woff2 (`Resources/assets/fonts/`)
- **Layout:** `theme::layouts.app` (`Resources/views/layouts/app.blade.php`)
- **DB rows:** seeded by `FlorialThemeSeeder` (savitarna/pardavimai/operacijos =
  default; web = switch-ready, `is_default=false`).

## Build (per-package Vite)

Node 18+ required.

```bash
cd packages/va-themes/florial
npm ci
npm run build          # → public/build/assets/florial.{css,js} + woff2 (tracked)
cd -
php artisan themes:publish-assets   # copies public/build → public/vendor/va-themes/florial/build (gitignored)
```

The `florial.{css,js}` bundle is emitted for the `savitarna.*` / `pardavimai.*` /
`operacijos.*` route patterns via the host `@assets` directive.

## Public marketing site — CMS Pages, not theme routes

Since **PackageStructure/task-16** the four landings (`/`, `/imonems`, `/erdvems`,
`/privatiems`) are `va-modules/pages` rows, not routes owned by this theme. What the
theme contributes is the presentation half:

| Piece | Where |
|---|---|
| Page templates (`florial-*`) | `Resources/views/pages/web/florial-{index,imonems,erdvems,privatiems}.blade.php` |
| The `/` homepage | `Resources/views/pages/home.blade.php` (`theme::pages.home` — resolves only while Florial is the active WEB theme, which is the gate that replaced the old route condition) |
| The data each template reads | `src/Support/LandingData.php`, handed over by a view composer |
| Landing URLs for the chrome | `src/Support/LandingLinks.php` — resolved from the page row by TEMPLATE, so an admin's slug rename moves every link |
| Template options + text slots | `webPageTemplates()` / `templateSlots()` on the provider |

The rows themselves are seeded by `va-florial/core`'s `FlorialLandingPagesSeeder`
(baseline, so `florial:upgrade --force` creates them).

⚠️ `Resources/views/pages` is prepended to the `pages` view namespace
**unconditionally** — the same call `va-themes/seglo` makes, and for the same reason:
a theme reaches that namespace only while it is the active WEB theme, and the landings
must render on a host where another theme owns `web`. Only `florial-*` names live
there, never `pages::web.default`, so nothing else is shadowed.

## Legal, DUK and Kontaktai — also CMS Pages (task-17)

**PackageStructure/task-17** did the same for the remaining six public URLs, and with
them went the LAST route this theme owned: there is no `Routes/web.php` any more. (The
`APP_ENV=testing` seams that survived it left in task-18 — see below.)

| Piece | Where |
|---|---|
| Page templates | `Resources/views/pages/web/florial-{privatumas,salygos,slapukai,pristatymo-salygos,duk,kontaktai}.blade.php` |
| Shared bodies | `Resources/views/legal/document.blade.php` (the four documents) · `Resources/views/legal/duk.blade.php` |
| The data each template reads | `src/Support/LegalData.php`, handed over by a view composer |
| Legal URLs for the chrome | `src/Support/LegalLinks.php` (over `src/Support/PageLinks.php`, shared with `LandingLinks`) |
| The contact FORM | `va-modules/forms` definition `florial-kontaktai`, rendered through `Resources/views/forms/web/variants/flor.blade.php` |
| The `/duk` override | `Resources/views/faq/web/index.blade.php` |

Two of the six URLs are **contested**, and the theme does not win them by force:

- **`/duk`** — `va-modules/faq` mounts its public index on exactly that URL, and a
  concrete route always beats the Pages catch-all. So the theme OVERRIDES that index
  (`faq::web.index`) instead of registering a competing route. Both entry points
  `@include('florial::legal.duk')`, so whichever wins draws the same page. (The module's
  EN route is `/en/faq`, so `/en/duk` still comes through the catch-all — which is why
  both are needed.)
- **`/kontaktai`** — other products seed their own `kontaktai` Page. `FlorialLegalPagesSeeder`
  therefore creates these two rows ONLY where Florial is the default `web` theme. That is
  not a new condition: it is the `florialOwnsHomepage()` gate their routes used to carry,
  moved from the routing table into the seeder.

⚠️ **The contact POST belongs to `va-modules/forms` (`/forms/florial-kontaktai`).** The
markup is unchanged — the `flor` variant renders the controls through the same
`Http\Forms\ContactForm` declaration the page used before — but the submission is now
validated from the stored definition, rate-limited, honeypot-guarded, PERSISTED and
mailed, where the theme's old handler wrote it to the log. Adding a field means editing
BOTH `ContactForm` (markup + rules) and `FlorialContactFormSeeder` (the stored definition);
`tests/Feature/Florial/LegalOwnershipTest` fails when they drift.

## This theme owns no PHP entry point at all (task-17 + task-18)

`src/Http/` contains `Forms/` and nothing else. There is no `Routes/` directory, no
`Controllers/` directory, no migration — which is the whole of what
[`.claude/module-structure.md`](../../../.claude/module-structure.md) §5 asks of a theme.

The last things to leave were the seven `/florial/testing/*` diagnostic endpoints
(**PackageStructure/task-18**). They are E2E scaffolding: Playwright drives a browser and
has no database handle, so a seam has to answer "did that submit really write a `Company`
row?" over HTTP. They now live in the Viartas monorepo under `tests/Support/Florial/`,
mapped by composer's autoload-**dev** entry, so a `composer install --no-dev` cannot
autoload them at all. They were always gated on `APP_ENV=testing`, so no deployment loses
a URL it had.

If you fork this theme and need those seams, copy them from the monorepo into your own
`tests/` — do NOT re-add them here. A theme that carries a controller carries a route
sooner or later, and a route in a theme silently shadows the CMS catch-all.

## Notes

- Brand tokens are **reconstructed** from the task spec because the design source
  `packages/viartas-design/ofisai/` is currently absent. Token NAMES are the
  stable contract — sync values 1:1 when the source returns.
- Flat radius (`--bs-border-radius: 0`) — magazine/editorial look.
