# Tech

## Stack
- **Framework:** Laravel 13, PHP 8.3+ (strict types).
- **Frontend:** Blade + Livewire + Alpine.js + Tailwind CSS, bundled with Vite.
- **Admin:** Filament (verify Laravel 13 support before committing; fallback:
  hand-built Livewire for critical screens).
- **Database:** MySQL 8 (InnoDB, foreign-key constraints enforced).
- **Auth:** Laravel Fortify (customer + admin guards).
- **Queue:** `database` driver (no Redis on shared hosting).
- **Cache / Session:** `database` or `file` driver.
- **Localization:** bilingual BM + English. UI strings via Laravel localization
  (`lang/ms`, `lang/en`, default `ms`); translatable DB content via
  **spatie/laravel-translatable** (locale-keyed JSON columns). Locale set by
  middleware, persisted in session, toggled by a language switcher.

## Interfaces with pluggable implementations
- `App\Payments\PaymentGateway` — payment provider (ToyyibPay / Billplz assumed;
  confirm actual). Keeps the rest of the app gateway-agnostic.
- `App\Services\GoldPriceProvider` — source of the current gold price (manual
  admin entry by default; optional scheduled API feed later).

## Frontend libraries (use only where they add clear value)
- SweetAlert2 — confirmations/toasts (destructive actions, form feedback).
- ApexCharts — admin dashboards/analytics.
- Lucide icons.
- AOS / subtle motion where suitable.
Reuse what's installed; do not add two libraries for the same job. Prefer
external CSS/JS over large inline blocks.

**Why spatie/laravel-translatable:** product names/descriptions, category names,
and CMS pages must exist in both BM and English. This package is the maintained
Laravel standard for translatable model attributes and avoids duplicate columns
(`name_en`, `name_ms`) or a hand-rolled translations table. UI strings still use
Laravel's built-in `lang/` files, no package needed.

## Design system (theme)
Brand identity: **royal purple + gold on white** — hex taken directly from the
live site's CSS custom properties (the WooCommerce theme tokens). White content
areas; purple carries the promo bar, hero, category overlays and testimonials;
gold is the accent for the logo, CTAs and display type. Elegant serif headings.
Tokens live as CSS custom properties in `resources/css/theme.css`, mapped into
Tailwind via `tailwind.config.js`. Never hardcode hex in components.

**Brand tokens** (verbatim from site CSS)
| Token | Hex | Use |
|---|---|---|
| `--primary` | `#720eec` | Brand purple (promo, hero, links, overlays) |
| `--on-primary` | `#fcfbfe` | Text on purple |
| `--highlight` | `#958e09` | UI highlight / state (e.g. ratings) — **not** a brand surface |
| `--content-bg` | `#ffffff` | Page / content background |
| `--ink` | `#515151` | Primary body text on white |
| `--subtext` | `#767676` | Secondary text / captions |
| `--secondary` | `#e9e6ed` | Light lavender-grey surfaces / product backdrop |

**State tokens** (from site CSS)
`--success #007518` · `--error #a00` · `--warning #ffba00` · `--info #2ea2cc`
· `--destructive #cc1818`

**Derived shades** (not in site CSS — for depth/metallic effects, same hue family)
| Token | Hex | Use |
|---|---|---|
| `--primary-deep` / `--primary-deeper` | `#5417b6` / `#421491` | Deepened purple for the large hero field + white-text contrast |
| `--gold` / `--gold-hi` / `--gold-mid` / `--gold-deep` | `#c4a35c` / `#ecdba9` / `#d2b573` / `#8f7233` | Warm **antique brand gold from the logo** — used for the identity: logo, ring, CTAs, headings |

> Two golds, on purpose: the visual identity uses the **warm antique gold**
> `#c4a35c` read from the logo. The site's CSS `--wc-highlight #958e09` is an
> olive UI-state token (ratings/highlights) — keep it only for that, not for
> brand surfaces.

Primary button = gold gradient (`--gold-hi → --gold-mid → --gold`) on dark text
(`#2f2c05`). Ghost button (on purple) = translucent white with a gold hairline,
gold on hover. Logo + display headings use the gold gradient.
> `--primary` `#720eec` is bright at full-bleed, so the large hero field uses the
> deepened shades above for readable white text; the brand token itself is
> unchanged.

**Typography** (chosen to match the AMALEENA JEWEL logo)
- Wordmark + small all-caps labels/eyebrows: **Cinzel** (600/700) — engraved
  classical capitals, the closest free match to the logo's lettering.
- Display headlines: **Cormorant Garamond** (600 / italic 500) — flowing,
  high-contrast serif for large hero/section titles; used with restraint.
- Body / UI / buttons / captions: **Jost** (300/400/500/600) — light geometric
  sans that echoes the logo's "JEWEL" subtitle.
- Loaded via Google Fonts (self-host for shared-hosting perf if needed).
- The header uses the real logo SVG in production; the text wordmark in the
  preview is a Cinzel stand-in.

**Motion**
- Ambient only, subtle: float ~6s, spotlight pulse ~5s, shine sweep ~3.6s,
  sparkle twinkle 2.2–3.9s (staggered), gem flash.
- Always honour `prefers-reduced-motion: reduce` (disable transforms/twinkles,
  keep a static composed state).

## Hero section (home)
The landing-page centrepiece. Split layout: copy left, animated jewellery right;
promo bar on top, slim header with logo + BM/EN toggle.
- **Signature:** on a deep-purple hero, an animated **SVG gold solitaire ring** —
  specular shine sweeps the band (masked to the ring), the diamond twinkles,
  sparkles drift, and the piece floats under a soft gold spotlight. Gold on purple
  mirrors the brand's own product photography. Pure SVG/CSS (no Lottie / no image
  assets) so it's light for cPanel.
- **Copy (bilingual):** eyebrow, headline (`home.hero.headline`), lede, two CTAs
  (`Beli Sekarang` / `Lihat Koleksi`), trust row. All strings via `lang/` files.
- **Responsive:** stacks on mobile with the ring on top.
- **A11y:** reduced-motion respected, visible keyboard focus on CTAs, `aria-label`
  on the ring SVG.
- Reference build: `hero-amaleena.html` (to be ported to
  `resources/views/components/hero.blade.php` + `<x-jewellery.ring/>`).

## Commands
```bash
# Setup
composer install
npm install
cp .env.example .env && php artisan key:generate
php artisan migrate --seed

# Dev
php artisan serve
npm run dev

# Build (assets compiled locally, uploaded to shared hosting)
npm run build

# Quality
./vendor/bin/pint            # PSR-12 formatting
php artisan test             # or ./vendor/bin/pest
```

## Deployment (cPanel / shared hosting)
- Point the domain docroot at `public/`; keep app + `.env` outside webroot.
- Upload **compiled** Vite assets (no Node build on the server).
- **Cron (every minute):** `php artisan schedule:run`.
- Scheduler runs the queue (short, idempotent jobs): e.g.
  `queue:work --stop-when-empty` each minute. Used for order emails, affiliate
  commission calc, notifications.
- Run `php artisan migrate --force` on deploy; `config:cache` + `route:cache`.

## Coding standards
- PSR-12 (enforced by Pint), Laravel conventions.
- `declare(strict_types=1);` and explicit return types on public methods.
- Backed enums for controlled values; constants otherwise.
- Money as integer cents; no floats for currency.
- Validate via Form Requests; authorize via policies/gates/middleware.
- No raw SQL unless clearly necessary; eager-load to avoid N+1; paginate lists.
- Add DB indexes/constraints where they aid integrity or performance.
- Handle errors gracefully; log useful context, never secrets or PII.

## Security defaults
- `APP_DEBUG=false` and generic error pages in production.
- Force HTTPS; secure + http-only + same-site cookies; DB-backed sessions.
- CSRF on all state-changing requests.
- Rate-limit auth, OTP, password reset, and checkout endpoints.
- Server-side price recomputation; snapshot prices onto orders.
- Atomic store-credit/voucher redemption (row lock, ledger, no double-spend).
- Validate uploads: mime + extension + size + dimensions; randomised filenames.
- Signature-verified, idempotent payment callbacks.
- Secrets only in `.env`; never committed. Passwords hashed (bcrypt/argon2).

## Config to confirm
- Payment gateway credentials/provider.
- Gold price source (manual vs API) and update cadence.
- Courier/tracking integration (if any) vs manual tracking numbers.
- Default locale = `ms` (BM), alternate `en`; confirm URL strategy (session-based
  switch vs `/en` prefix).
