Output:
Chunk ID: db5744
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 5120
Output:
## 3. Products List

**Route:** `GET /admin/products`

**Livewire component:** `App\Livewire\Admin\Products\Index`

**Blade view:** `resources/views/livewire/admin/products/index.blade.php`

### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `search` | `string` | Search query, live-debounced at 300ms |
| `statusFilter` | `string` | Status filter: `all`, `draft`, `active`, `archived` |
| `typeFilter` | `string` | Product type filter |
| `selectedIds` | `array<int>` | Array of selected product IDs for bulk actions |
| `selectAll` | `bool` | Whether all visible rows are selected |
| `sortField` | `string` | Column to sort by (default: `updated_at`) |
| `sortDirection` | `string` | `asc` or `desc` |

| Action | Behavior |
|--------|----------|
| `updatedSearch()` | Resets pagination |
| `updatedStatusFilter()` | Resets pagination |
| `updatedTypeFilter()` | Resets pagination |
| `bulkArchive()` | Archives all selected products |
| `bulkDelete()` | Soft-deletes selected products (with confirmation) |
| `bulkSetActive()` | Sets selected products to active status |
| `sortBy(string $field)` | Toggles sort direction or changes sort field |
| `toggleSelectAll()` | Selects or deselects all visible product IDs |
| `confirmBulkDelete()` | Opens delete confirmation modal |

| Computed Property | Purpose |
|-------------------|---------|
| `products` | Paginated query result with search, filters, sorting applied. Eager loads `variants`, `media` (limit 1). Uses `withCount('variants')`. |
| `productTypes` | Distinct product types for the type filter dropdown |

### Wireframe

```
+------------------------------------------------------------------+
| Products                                    [+ Add product]       |
+------------------------------------------------------------------+
| [search icon] Search products...   [Status: All v] [Type: All v] |
+------------------------------------------------------------------+
| (shown when items selected)                                       |
| "3 products selected"  [Set Active]  [Archive]  [Delete]         |
+------------------------------------------------------------------+
| [x] | IMG | Title ^     | Status  | Inventory | Type | Vendor | Updated ^  |
|-----|-----|-------------|---------|-----------|------|--------|-----------|
| [ ] | img | Blue Shirt  | Active  | 45        | Tops | Nike   | 2h ago    |
| [ ] | img | Red Hat     | Draft   | 12        | Hats | Adidas | 1d ago    |
| [ ] | img | Sneakers    | Active  | 0         | Shoes| Nike   | 3d ago    |
| ...                                                              |
+------------------------------------------------------------------+
| < 1 2 3 ... 10 >  (pagination)                                  |
+------------------------------------------------------------------+
```

### Table Column Definitions

| Column | Sortable | Content |
|--------|----------|---------|
| Select | No | Checkbox for bulk selection |
| Image | No | 40x40 rounded thumbnail of first media item, or placeholder icon |
| Title | Yes | Product title as a link to the edit page (uses Livewire navigate) |
| Status | No | Flux badge: draft=zinc/gray, active=green, archived=red |
| Inventory | Yes | Sum of `inventory_items.quantity_on_hand` across all variants |
| Type | No | Product type text or "-" |
| Vendor | No | Vendor name text or "-" |
| Updated | Yes (default) | Relative timestamp (e.g., "2h ago") |

### Bulk Action Bar

Conditionally shown when `count($selectedIds) > 0`. Contains:
- Text showing "X products selected"
- "Set Active" button (ghost variant)
- "Archive" button (ghost variant)
- "Delete" button (danger variant) - opens confirmation modal

### Empty State

When no products exist (not filtered, genuinely empty):
- Centered container with a large muted placeholder icon (cube)
- Heading: "Add your first product"
- Text: "Start building your catalog by adding products."
- Primary button: "Add product" linking to the create page

### Delete Confirmation Modal

| Property | Value |
|----------|-------|
| Modal name | `confirm-bulk-delete` |
| Max width | md |
| Heading | "Delete products?" |
| Body text | "This will archive X product(s). Products with orders cannot be permanently deleted." |
| Cancel button | Ghost variant, closes modal |
| Confirm button | Danger variant, calls `bulkDelete` |

### Flux Component Mapping

| UI Element | Flux Component |
|-----------|----------------|
| Page title | `heading` |
| Add product button | `button` (variant: primary) |
| Search field | `input` (icon: magnifying-glass) |
| Status filter | `select` |
| Type filter | `select` |
| Bulk action buttons | `button` (ghost/danger variants) |
| Selected count text | `text` |
| Row checkbox | `checkbox` |
| Status badges | `badge` |
| Cell text | `text` |
| Delete confirmation | `modal` |
| Pagination | Laravel default Tailwind pagination |

### View States

| State | Behavior |
|-------|----------|
| Loading | Table body shows reduced opacity |
| Empty (no products) | Empty state with illustration and CTA |
| Empty (filtered) | Table with "No products match your filters" message |
| Populated | Table with data rows and pagination |

---

## 4. Product Create/Edit

**Route:** `GET /admin/products/create` and `GET /admin/products/{product}/edit`

**Livewire component:** `App\Livewire\Admin\Products\Form`

**Blade view:** `resources/views/livewire/admin/products/form.blade.php`

### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `product` | `?Product` | The product being edited, null for create |
| `title` | `string` | Product title |
| `descriptionHtml` | `string` | Product description (HTML) |
| `status` | `string` | `draft`, `active`, `archived` |
| `vendor` | `string` | Vendor name |
| `productType` | `string` | Product type string |
| `tags` | `string` | Comma-separated tags |
| `handle` | `string` | URL handle/slug |
| `publishedAt` | `?string` | Publish date (Y-m-d H:i) |
| `collectionIds` | `array<int>` | Selected collection IDs |
| `options` | `array` | Options array: `[{name: string, values: string[]}]` |
| `variants` | `array` | Variants array: `[{sku, price, compareAtPrice, quantity, requiresShipping, optionValues}]` |
| `media` | `array` | Existing media items: `[{id, url, alt_text, position}]` |
| `newMedia` | `array` | Temporary uploaded files |

| Action | Behavior |
|--------|----------|
| `mount(?Product $product)` | Loads product data into properties or sets defaults for create |
| `save()` | Validates and saves/creates the product, variants, options, and media |
| `addOption()` | Adds a new empty option `{name: '', values: []}` |
| `removeOption(int $index)` | Removes an option and regenerates variants |
| `addOptionValue(int $optionIndex)` | Adds a value to an option |
| `removeOptionValue(int $optionIndex, int $valueIndex)` | Removes a value |
| `generateVariants()` | Generates variant combinations from current options |
| `uploadMedia()` | Handles file upload via Livewire temporary upload |
| `removeMedia(int $mediaId)` | Removes a media item |
| `reorderMedia(array $order)` | Updates media positions |
| `updateMediaAlt(int $mediaId, string $alt)` | Updates alt text for a media item |
| `deleteProduct()` | Archives the product (with confirmation modal) |

| Computed Property | Purpose |
|-------------------|---------|
| `availableCollections` | All collections for the current store |
| `isEditing` | `bool` - whether editing or creating |

### Validation Rules

| Field | Rules |
|-------|-------|
| `title` | Required, string, max 255 |
| `descriptionHtml` | Nullable, string, max 65535 |
| `status` | Required, one of: draft, active, archived |
| `vendor` | Nullable, string, max 255 |
| `productType` | Nullable, string, max 255 |
| `tags` | Nullable, string |
| `handle` | Required, string, max 255, unique (excluding current product) |
| `variants.*.sku` | Nullable, string, max 255 |
| `variants.*.price` | Required, integer, min 0 |
| `variants.*.compareAtPrice` | Nullable, integer, min 0 |
| `variants.*.quantity` | Required, integer, min 0 |

### Two-Column Layout Wireframe

```
+------------------------------------------+-------------------+
|          LEFT COLUMN (2/3)               | RIGHT COLUMN (1/3)|
+------------------------------------------+-------------------+
|                                          |                   |
|  TITLE                                   |  STATUS CARD      |
|  +------------------------------------+  |  Status: [Draft v]|
|  | [Short Sleeve T-Shirt           ]  |  |                   |
|  +------------------------------------+  |  PUBLISHING CARD  |
|                                          |  Published at:    |
|  DESCRIPTION                             |  [datetime input]  |
|  +------------------------------------+  |                   |
|  | Describe your product...           |  |  ORGANIZATION     |
|  |                                    |  |  Vendor:          |
|  |                                    |  |  [Nike          ] |
|  |                                    |  |  Product type:    |
|  +------------------------------------+  |  [T-Shirts      ] |
|                                          |  Tags:            |
|  MEDIA                                   |  [summer, cotton ] |
|  +------------------------------------+  |  (comma-separated)|
|  | +--+ +--+ +--+ +--+               |  |                   |
|  | |  | |  | |  | |  |  (thumbnails) |  |  COLLECTIONS      |
|  | +--+ +--+ +--+ +--+               |  |  [x] Summer Sale  |
|  |                                    |  |  [ ] Winter       |
|  | [--- Drag & drop or click ---]    |  |  [x] New Arrivals |
|  +------------------------------------+  |                   |
|                                          |                   |
|  VARIANTS                                |                   |
|  Option name: [Size    ]                 |                   |
|  Values:      [S, M, L, XL         ]    |                   |
|  [trash]                                 |                   |
|                                          |                   |
|  [+ Add another option]                  |                   |
|                                          |                   |
|  Variant | SKU    | Price | Compare | Qty | Ship |          |
|  S / Red  | [   ] | [   ] | [     ] | [ ] | [x]  |          |
|  M / Red  | [   ] | [   ] | [     ] | [ ] | [x]  |          |
|  L / Red  | [   ] | [   ] | [     ] | [ ] | [x]  |          |
|                                          |                   |
|  SEO (collapsible)                       |                   |
|  > Search engine listing                 |                   |
|    URL handle: [short-sleeve-t-shirt]    |                   |
+------------------------------------------+-------------------+

+==================================================================+
| STICKY SAVE BAR (fixed bottom)                                    |
|                                      [Discard]  [Save]           |
+==================================================================+
```

### Left Column Sections

#### Title Input
- Flux field with label "Title"
- Flux input, placeholder "Short Sleeve T-Shirt"
- Flux error display for validation

#### Description
- Flux field with label "Description"
- Flux textarea, 8 rows, placeholder "Describe your product..."
- Future enhancement: WYSIWYG toolbar via Alpine.js; baseline is plain textarea

#### Media Section
A card with heading "Media" (size md).

**Upload area:** A dashed-border drop zone with upload icon and text "Drag and drop images or click to upload". A hidden file input (multiple, accept images) triggered by clicking the zone.

**Upload progress:** A progress bar shown during file upload.

**Media grid:** A 4-column grid (responsive: 2 on small, 3 on sm, 4 on lg) of thumbnails:
- Each item: square aspect ratio, object-cover, rounded
- On hover overlay shows: delete button (X icon, top-right), alt text edit button (pencil icon, bottom-left)
- Drag-to-reorder supported via Livewire sortable or Alpine.js Sortable

#### Variants Section
A card with heading "Variants" (size md).

**Options builder:** For each option, a row containing:
- "Option name" input field (placeholder: "Size")
- "Values" input field (placeholder: "S, M, L, XL" - comma-separated)
- Trash button to remove the option

Below options: "Add another option" ghost button with plus icon.

When options change, `generateVariants()` is called automatically via lifecycle hook.

**Variants table:** Shown when variants exist. Scrollable table with inline-editable fields:

| Column | Input Type | Description |
|--------|-----------|-------------|
| Variant | Read-only text | Combination name, e.g. "S / Red" |
| SKU | Text input (small) | Stock keeping unit |
| Price | Number input (small) | Price in major units |
| Compare at price | Number input (small) | Original price for sale display |
| Quantity | Number input (small) | On-hand inventory |
| Requires shipping | Checkbox | Whether variant needs shipping |

#### SEO Section
A collapsible section (collapsed by default, expands with animation):
- Toggle button: chevron icon + "Search engine listing" text
- When expanded: URL handle input field with placeholder "short-sleeve-t-shirt" and validation error display

### Right Column Sections

#### Status Card
- Flux field with label "Status"
- Flux select with options: Draft, Active, Archived

#### Publishing Card
- Flux field with label "Published at"
- Datetime-local input

#### Product Organization Card
- Vendor: Flux input, placeholder "Nike"
- Product type: Flux input, placeholder "T-Shirts"
- Tags: Flux input, placeholder "summer, cotton, sale" with description "Separate tags with commas"

#### Collections Card
- Flux field with label "Collections"
- For each available collection: a row with checkbox and collection title text
- Checkbox values bound to `collectionIds`

### Sticky Save Bar

Fixed at the bottom of the viewport. Appears when changes are detected.
- Left-aligned to account for sidebar width on desktop
- "Discard" button (ghost variant) - links back to products index
- "Save" button (primary variant) - calls `save()`, shows "Saving..." text during loading, disabled while loading

### Delete Confirmation Modal (Edit Only)

| Property | Value |
|----------|-------|
| Modal name | `confirm-delete-product` |
| Max width | md |
| Heading | "Delete this product?" |
| Body text | "This product will be archived. Products with existing orders cannot be permanently removed." |
| Cancel button | Ghost variant, closes modal |
| Confirm button | Danger variant, calls `deleteProduct` |

### Flux Component Mapping

| UI Element | Flux Component |
|-----------|----------------|
| Page title | `heading` |
| Section titles | `heading` (size md) |
| All form labels | `field`, label via Flux |
| Text inputs | `input` |
| Textarea | `textarea` |
| Status/type selects | `select` |
| Checkboxes | `checkbox` |
| Validation errors | `field` error display |
| Tags description | `field` description |
| Save/discard buttons | `button` |
| Delete confirmation | `modal` |
| Section dividers | `separator` |
| Variant icons | `icon` |
| Status text | `text` |

### View States

| State | Behavior |
|-------|----------|
| Loading (save) | Save button disabled, text changes to "Saving..." |
| Loading (upload) | Progress bar shown in media section |
| Create mode | Empty form, no delete button, heading says "Add product" |
| Edit mode | Pre-filled form, delete button visible, heading shows product title |
| Validation error | Inline error messages below each invalid field |

---

## 5. Collections List and Edit

### 5.1 Collections List

**Route:** `GET /admin/collections`

**Livewire component:** `App\Livewire\Admin\Collections\Index`

**Blade view:** `resources/views/livewire/admin/collections/index.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `search` | `string` | Search query |
| `statusFilter` | `string` | `all`, `active`, `archived` |

| Action | Behavior |
|--------|----------|
| `updatedSearch()` | Resets pagination |
| `deleteCollection(int $id)` | Deletes a collection after confirmation |

| Computed Property | Purpose |
|-------------------|---------|
| `collections` | Paginated, filtered, searched. Includes `withCount('products')`. |

#### Layout

Same pattern as Products list:
- Heading "Collections" with "Add collection" primary button
- Search input
- Status filter dropdown

#### Table Columns

| Column | Content |
|--------|---------|
| Title | Link to edit page |
| Products count | Number of products in collection |
| Status | Flux badge |
| Updated date | Relative timestamp |

**Empty state:** "Create your first collection" with CTA button.

### 5.2 Collection Edit/Create

**Route:** `GET /admin/collections/create` and `GET /admin/collections/{collection}/edit`

**Livewire component:** `App\Livewire\Admin\Collections\Form`

**Blade view:** `resources/views/livewire/admin/collections/form.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `collection` | `?Collection` | The collection being edited |
| `title` | `string` | Collection title |
| `handle` | `string` | URL slug |
| `descriptionHtml` | `string` | Collection description |
| `status` | `string` | `active` or `archived` |
| `productSearch` | `string` | Search input for product assignment |
| `assignedProductIds` | `array<int>` | Ordered list of product IDs in this collection |

| Action | Behavior |
|--------|----------|
| `save()` | Validates and saves the collection with product assignments |
| `addProduct(int $productId)` | Adds a product to the collection |
| `removeProduct(int $productId)` | Removes a product from the collection |
| `reorderProducts(array $order)` | Updates product positions |

| Computed Property | Purpose |
|-------------------|---------|
| `searchResults` | Products matching `productSearch` that are not already assigned |
| `assignedProducts` | Ordered collection of assigned Product models |

#### Layout Wireframe

```
+------------------------------------------+-------------------+
|          LEFT COLUMN (2/3)               | RIGHT COLUMN (1/3)|
+------------------------------------------+-------------------+
|  Title: [Summer Collection         ]     | Status: [Active v] |
|  Handle: [summer-collection        ]     |                   |
|  Description:                            |                   |
|  [textarea...                      ]     |                   |
|                                          |                   |
|  PRODUCTS                                |                   |
|  Search: [Search products...       ]     |                   |
|  +----------------------------------+   |                   |
|  | Blue Shirt           [Add]       |   |                   |
|  | Red Hat              [Add]       |   |                   |
|  +----------------------------------+   |                   |
|                                          |                   |
|  Assigned products:                      |                   |
|  [drag] [img] Sneakers         [remove]  |                   |
|  [drag] [img] Blue Shirt       [remove]  |                   |
+------------------------------------------+-------------------+

+==================================================================+
| STICKY SAVE BAR                          [Discard]  [Save]       |
+==================================================================+
```

**Left column:**
- Title input
- Handle input
- Description textarea
- Products assignment section:
  - Search input with live debounce (300ms)
  - Search results dropdown (conditionally shown): list of matching products with "Add" button
  - Assigned products list: each row shows product title, thumbnail, and a remove button. Rows are sortable via drag-to-reorder.

**Right column:**
- Status: Flux select with Active / Archived
- Sticky save bar (same pattern as product form)

---

## 6. Inventory Management

**Route:** `GET /admin/inventory`

**Livewire component:** `App\Livewire\Admin\Inventory\Index`

**Blade view:** `resources/views/livewire/admin/inventory/index.blade.php`

### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|


sed -n '376,740p' specs/04-STOREFRONT-UI.md

## 4. Collection Page (GET /collections/{handle})

**Livewire Component:** CollectionPage

### 4.1 Collection Header

| Element | Description |
|---|---|
| Breadcrumbs | Home > Collections > {Collection Title} |
| Title | H1, large, bold |
| Description | Rendered HTML from collection description, styled with prose/typography, constrained width |
| Spacing | Generous vertical padding |

### 4.2 Toolbar

Horizontal bar between the header and the product grid.

**Desktop:**

```
+--------------------------------------------------------------+
| [Filter toggle button]          [X products] [Sort dropdown]  |
+--------------------------------------------------------------+
```

**Mobile:**

```
+--------------------------------------------------------------+
| [Filter button]                           [Sort dropdown]     |
+--------------------------------------------------------------+
```

| Element | Description |
|---|---|
| Product count | Small muted text, e.g., "24 products" |
| Sort dropdown | Select element with live Livewire binding |

**Sort Options:**

| Option | Description |
|---|---|
| Featured | Default sort order |
| Price: Low to High | Ascending by price |
| Price: High to Low | Descending by price |
| Newest | Descending by creation date |
| Best Selling | Descending by sales count |

### 4.3 Filter Sidebar

**Desktop (large breakpoint and above):** Persistent sidebar on the left, approximately 256px wide.
**Mobile (below large breakpoint):** Slide-out drawer from the left, triggered by "Filter" button with filter icon.

**Filter Groups:**

Each filter group is a collapsible section with a heading and toggle chevron.

| Filter Group | Type | Binding | Description |
|---|---|---|---|
| Availability | Single checkbox | Live | "In stock" toggle |
| Price Range | Two number inputs | Live with 500ms debounce | "Min" and "Max" inputs with currency symbol prefix, displayed side by side |
| Product Type | Checkbox list | Live | Dynamically sourced from products in the collection |
| Vendor | Checkbox list | Live | Dynamically sourced from products in the collection |

**Clear All:**

- Link at the top of the filter sidebar: "Clear all filters"
- Only visible when at least one filter is active
- Small, blue, interactive text

**Active Filter Pills:**

- Displayed above the product grid (below toolbar) when filters are active
- Each pill: rounded tag with filter value text and a remove (X) button
- "Clear all" link at the end of the pills row

**Mobile Filter Drawer:**

| Attribute | Description |
|---|---|
| Position | Full-height overlay from left side |
| Header | "Filters" title + close button (X) |
| Content | Same filter groups as desktop, stacked vertically |
| Footer | Sticky "Apply" button at bottom that closes the drawer |
| Clear | "Clear all" link in header area |
| Focus | Trapped within drawer while open |

### 4.4 Product Grid

| Attribute | Description |
|---|---|
| Layout | Fills remaining width next to the sidebar |
| Columns | 2 on mobile, 3 on tablet, 3 on desktop (when sidebar visible). Without sidebar: 2 mobile, 3 tablet, 4 desktop |
| Loading state | Subtle opacity reduction on the grid while results update |

### 4.5 Product Card (Component)

The product card is a reusable component used on collection pages, search results, and featured products sections.

```
+---------------------------+
|  [Product Image]          |
|  1:1 aspect ratio         |
|  cover-fit, rounded       |
|  (hover: show 2nd image)  |
|                           |
|  [Sale badge]  [Sold out] |
+---------------------------+
|  [Product Title]          |
|  [Price]  [Compare price] |
|  [Quick Add / Options]    |
+---------------------------+
```

**Image Behavior:**

| Attribute | Description |
|---|---|
| Container | Square aspect ratio, rounded corners, overflow hidden, gray placeholder background |
| Primary image | Cover-fit, fills the container |
| Secondary image | On hover (desktop with hover capability only), crossfades to the second product image |
| Fallback | If no image, show a centered shopping bag placeholder icon |
| Loading | Lazy-loaded |

**Badges:**

| Badge | Condition | Position |
|---|---|---|
| "Sale" | compare_at_amount > price_amount | Top-left of image, stacked vertically |
| "Sold out" | All variants out of stock with deny policy | Top-left of image, stacked vertically |

**Text Content:**

| Element | Description |
|---|---|
| Title | H3 (or configurable heading level), small semi-bold text. Truncated to 2 lines |
| Price | Current price in small semi-bold text. Compare-at price shown with strikethrough if applicable |
| Quick Add | Single variant: "Add to cart" button. Multiple variants: "Choose options" link text. Hidden by default on desktop (shown on hover), always visible on mobile |

**Accessibility:**

- Card link wraps the entire card; title serves as the accessible name
- Badges have screen reader labels (e.g., "On sale")
- Price changes announced via parent live region…2838 chars truncated…nail strip | Horizontal row with overflow scroll, small gap between thumbnails |
| Each thumbnail | 64x64px, rounded, cover-fit, pointer cursor. Active thumbnail has a colored border |
| Variant images | When a variant is selected that has specific images, those images are shown |

**Mobile:**

| Attribute | Description |
|---|---|
| Gallery | Horizontal scrollable row with snap-scroll behavior |
| Each image | Full-width, square aspect ratio, rounded, cover-fit |
| Scroll indicators | Row of dots below the gallery. Active dot is dark (light in dark mode), inactive dots are gray |

**Accessibility:**

| Requirement | Description |
|---|---|
| Alt text | Main image has descriptive alt text from product media data |
| Thumbnails | Rendered as buttons with labels "View image X of Y" and current state indicator |
| Region | Gallery wrapped in a labeled region ("Product images") |

### 5.3 Product Info (Right Column)

#### Breadcrumbs

Home > {Primary Collection} > {Product Title}. Small, muted text with separator characters.

#### Product Title

H1, large, bold.

#### Price Display

| Element | Description |
|---|---|
| Current price | Large, bold |
| Compare-at price | Medium, muted, strikethrough (shown when set and higher than current price) |
| Sale badge | Inline badge next to the price |
| Currency | Formatted based on the store's default currency |
| Live updates | Price container is a live region, so price changes when variant changes are announced to screen readers |

#### Variant Selector

For each product option (e.g., Size, Color), render a selector group.

**Radio button style (for options with 6 or fewer values):**

```
Size
[S] [M] [L] [XL] [XXL]
```

| State | Visual Description |
|---|---|
| Available | Bordered pill shape, pointer cursor, border darkens on hover |
| Selected | Primary color border with subtle background tint and focus ring |
| Unavailable | Muted border, muted text, strikethrough, reduced opacity, not-allowed cursor |

**Select dropdown style:** Used when the option has more than 6 values.

**Color swatches (when option name is "Color"):**

| Attribute | Description |
|---|---|
| Shape | Circular, 32x32px |
| Color | Determined by value name mapped to a color lookup or inline style |
| Selected state | Ring with offset around the swatch |
| Tooltip | Shows color name on hover |

**Livewire behavior:**

- Selected options are a key-value map (e.g., Size = M, Color = Red)
- Selected variant is computed by matching all selected options to the variant option values
- On change: updates price, stock messaging, available images, and add-to-cart availability

#### Stock Messaging

Displayed below the variant selector.

| Condition | Message | Visual Style |
|---|---|---|
| In stock, quantity > 10 | "In stock" | Green text with check icon |
| In stock, quantity <= 10 | "Only X left in stock" | Amber text with warning icon |
| Out of stock, deny policy | "Out of stock" | Red text with X icon |
| Out of stock, continue policy | "Available on backorder" | Blue text with info icon |

Small text, icon + text inline. Wrapped in a live region for screen reader announcements on variant change.

#### Quantity Selector

Uses the quantity-selector component.

```
[-] [3] [+]
```

| Attribute | Description |
|---|---|
| Container | Inline flex, bordered, rounded |
| Decrease button | "-" icon, 40x40px. Disabled at minimum (1) |
| Input | Centered number input, 56px wide, 40px tall. Min = 1, Max = available stock (deny policy) or unlimited (continue policy) |
| Increase button | "+" icon, same size. Disabled at maximum |
| Disabled state | Reduced opacity, not-allowed cursor |
| Labels | Input labeled "Quantity", buttons labeled "Decrease quantity" / "Increase quantity" |

#### Add to Cart Button

| Attribute | Description |
|---|---|
| Width | Full width |
| Style | Primary button: blue background, white text, semi-bold, generous padding, rounded |
| Text | "Add to cart" |
| Loading state | Text changes to "Adding..." with spinner icon. Button disabled during loading |
| Sold out state | Gray background, muted text, not-allowed cursor. Text: "Sold out" |
| Spacing | Generous top margin |

**Livewire action (addToCart):**

1. Validates variant is selected and in stock
2. Adds to cart (creates cart if none exists in session)
3. Dispatches "cart-updated" browser event
4. Cart drawer opens automatically

#### Product Description

- Separated from above content by a horizontal rule with generous vertical spacing
- Rendered HTML styled with prose/typography styling

#### Tags

- Displayed below the description (if tags exist)
- Horizontal wrapping row with gap
- Each tag: rounded pill, gray background, small text

### 5.4 Accessibility Summary

| Requirement | Implementation |
|---|---|
| Variant radio buttons | Properly labeled with labels and grouped with fieldset/legend |
| Live regions | Price display, stock messaging, add-to-cart feedback |
| Image alt text | All images have meaningful alt text |


Output:
Chunk ID: 9d7eda
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 3210
Output:
## 4. Collection Page (GET /collections/{handle})

**Livewire Component:** CollectionPage

### 4.1 Collection Header

| Element | Description |
|---|---|
| Breadcrumbs | Home > Collections > {Collection Title} |
| Title | H1, large, bold |
| Description | Rendered HTML from collection description, styled with prose/typography, constrained width |
| Spacing | Generous vertical padding |

### 4.2 Toolbar

Horizontal bar between the header and the product grid.

**Desktop:**

```
+--------------------------------------------------------------+
| [Filter toggle button]          [X products] [Sort dropdown]  |
+--------------------------------------------------------------+
```

**Mobile:**

```
+--------------------------------------------------------------+
| [Filter button]                           [Sort dropdown]     |
+--------------------------------------------------------------+
```

| Element | Description |
|---|---|
| Product count | Small muted text, e.g., "24 products" |
| Sort dropdown | Select element with live Livewire binding |

**Sort Options:**

| Option | Description |
|---|---|
| Featured | Default sort order |
| Price: Low to High | Ascending by price |
| Price: High to Low | Descending by price |
| Newest | Descending by creation date |
| Best Selling | Descending by sales count |

### 4.3 Filter Sidebar

**Desktop (large breakpoint and above):** Persistent sidebar on the left, approximately 256px wide.
**Mobile (below large breakpoint):** Slide-out drawer from the left, triggered by "Filter" button with filter icon.

**Filter Groups:**

Each filter group is a collapsible section with a heading and toggle chevron.

| Filter Group | Type | Binding | Description |
|---|---|---|---|
| Availability | Single checkbox | Live | "In stock" toggle |
| Price Range | Two number inputs | Live with 500ms debounce | "Min" and "Max" inputs with currency symbol prefix, displayed side by side |
| Product Type | Checkbox list | Live | Dynamically sourced from products in the collection |
| Vendor | Checkbox list | Live | Dynamically sourced from products in the collection |

**Clear All:**

- Link at the top of the filter sidebar: "Clear all filters"
- Only visible when at least one filter is active
- Small, blue, interactive text

**Active Filter Pills:**

- Displayed above the product grid (below toolbar) when filters are active
- Each pill: rounded tag with filter value text and a remove (X) button
- "Clear all" link at the end of the pills row

**Mobile Filter Drawer:**

| Attribute | Description |
|---|---|
| Position | Full-height overlay from left side |
| Header | "Filters" title + close button (X) |
| Content | Same filter groups as desktop, stacked vertically |
| Footer | Sticky "Apply" button at bottom that closes the drawer |
| Clear | "Clear all" link in header area |
| Focus | Trapped within drawer while open |

### 4.4 Product Grid

| Attribute | Description |
|---|---|
| Layout | Fills remaining width next to the sidebar |
| Columns | 2 on mobile, 3 on tablet, 3 on desktop (when sidebar visible). Without sidebar: 2 mobile, 3 tablet, 4 desktop |
| Loading state | Subtle opacity reduction on the grid while results update |

### 4.5 Product Card (Component)

The product card is a reusable component used on collection pages, search results, and featured products sections.

```
+---------------------------+
|  [Product Image]          |
|  1:1 aspect ratio         |
|  cover-fit, rounded       |
|  (hover: show 2nd image)  |
|                           |
|  [Sale badge]  [Sold out] |
+---------------------------+
|  [Product Title]          |
|  [Price]  [Compare price] |
|  [Quick Add / Options]    |
+---------------------------+
```

**Image Behavior:**

| Attribute | Description |
|---|---|
| Container | Square aspect ratio, rounded corners, overflow hidden, gray placeholder background |
| Primary image | Cover-fit, fills the container |
| Secondary image | On hover (desktop with hover capability only), crossfades to the second product image |
| Fallback | If no image, show a centered shopping bag placeholder icon |
| Loading | Lazy-loaded |

**Badges:**

| Badge | Condition | Position |
|---|---|---|
| "Sale" | compare_at_amount > price_amount | Top-left of image, stacked vertically |
| "Sold out" | All variants out of stock with deny policy | Top-left of image, stacked vertically |

**Text Content:**

| Element | Description |
|---|---|
| Title | H3 (or configurable heading level), small semi-bold text. Truncated to 2 lines |
| Price | Current price in small semi-bold text. Compare-at price shown with strikethrough if applicable |
| Quick Add | Single variant: "Add to cart" button. Multiple variants: "Choose options" link text. Hidden by default on desktop (shown on hover), always visible on mobile |

**Accessibility:**

- Card link wraps the entire card; title serves as the accessible name
- Badges have screen reader labels (e.g., "On sale")
- Price changes announced via parent live region when filtering changes results

### 4.6 Pagination

| Attribute | Description |
|---|---|
| Style | Numbered pagination with previous/next arrow buttons |
| Current page | Highlighted with primary color background and white text |
| Other pages | Muted text, hover background |
| Disabled | Reduced opacity and not-allowed cursor on previous (at first page) or next (at last page) |
| Truncation | Ellipsis for large page counts |
| Mobile | Simplified: previous/next only with current page indicator |

### 4.7 Empty State

When no products match the current filters:

```
+--------------------------------------------------------------+
|  [Search icon - large, muted]                                 |
|  "No products found"                                          |
|  "Try adjusting your filters or browse our full collection."  |
|  [Clear filters button]                                       |
+--------------------------------------------------------------+
```

| Element | Description |
|---|---|
| Icon | Large, muted search icon |
| Heading | Medium, semi-bold |
| Subtext | Muted, descriptive guidance |
| Button | Secondary style, "Clear filters" |
| Position | Centered in the grid area with generous vertical padding |

---

## 5. Product Page (GET /products/{handle})

**Livewire Component:** ProductPage

### 5.1 Layout

**Desktop (large breakpoint and above):** Two-column, 50/50 split. Left column (images) is sticky while the right column (product info) scrolls.

**Mobile:** Single column, stacked. Images first, then product info.

```
Desktop:
+-------------------------------+-------------------------------+
|  [Image Gallery]              |  [Product Info]               |
|  (sticky while scrolling)     |  (scrollable)                 |
+-------------------------------+-------------------------------+

Mobile:
+--------------------------------------------------------------+
|  [Image Gallery - horizontal scroll]                          |
+--------------------------------------------------------------+
|  [Product Info - full width]                                  |
+--------------------------------------------------------------+
```

Container constrained to standard max-width with horizontal padding and vertical spacing.

### 5.2 Image Gallery (Left Column)

**Desktop:**

```
+-------------------------------+
|                               |
|  [Main Image - large]         |
|  1:1 aspect ratio, rounded    |
|                               |
+-------------------------------+
| [Thumb] [Thumb] [Thumb] ...  |
+-------------------------------+
```

| Attribute | Description |
|---|---|
| Main image | Square aspect ratio, large rounded corners, cover-fit. Gray placeholder background |
| Zoom on hover | Optional (theme setting): image scales to 150% and follows cursor position |
| Thumbnail strip | Horizontal row with overflow scroll, small gap between thumbnails |
| Each thumbnail | 64x64px, rounded, cover-fit, pointer cursor. Active thumbnail has a colored border |
| Variant images | When a variant is selected that has specific images, those images are shown |

**Mobile:**

| Attribute | Description |
|---|---|
| Gallery | Horizontal scrollable row with snap-scroll behavior |
| Each image | Full-width, square aspect ratio, rounded, cover-fit |
| Scroll indicators | Row of dots below the gallery. Active dot is dark (light in dark mode), inactive dots are gray |

**Accessibility:**

| Requirement | Description |
|---|---|
| Alt text | Main image has descriptive alt text from product media data |
| Thumbnails | Rendered as buttons with labels "View image X of Y" and current state indicator |
| Region | Gallery wrapped in a labeled region ("Product images") |

### 5.3 Product Info (Right Column)

#### Breadcrumbs

Home > {Primary Collection} > {Product Title}. Small, muted text with separator characters.

#### Product Title

H1, large, bold.

#### Price Display

| Element | Description |
|---|---|
| Current price | Large, bold |
| Compare-at price | Medium, muted, strikethrough (shown when set and higher than current price) |
| Sale badge | Inline badge next to the price |
| Currency | Formatted based on the store's default currency |
| Live updates | Price container is a live region, so price changes when variant changes are announced to screen readers |

#### Variant Selector

For each product option (e.g., Size, Color), render a selector group.

**Radio button style (for options with 6 or fewer values):**

```
Size
[S] [M] [L] [XL] [XXL]
```

| State | Visual Description |
|---|---|
| Available | Bordered pill shape, pointer cursor, border darkens on hover |
| Selected | Primary color border with subtle background tint and focus ring |
| Unavailable | Muted border, muted text, strikethrough, reduced opacity, not-allowed cursor |

**Select dropdown style:** Used when the option has more than 6 values.

**Color swatches (when option name is "Color"):**

| Attribute | Description |
|---|---|
| Shape | Circular, 32x32px |
| Color | Determined by value name mapped to a color lookup or inline style |
| Selected state | Ring with offset around the swatch |
| Tooltip | Shows color name on hover |

**Livewire behavior:**

- Selected options are a key-value map (e.g., Size = M, Color = Red)
- Selected variant is computed by matching all selected options to the variant option values
- On change: updates price, stock messaging, available images, and add-to-cart availability

#### Stock Messaging

Displayed below the variant selector.

| Condition | Message | Visual Style |
|---|---|---|
| In stock, quantity > 10 | "In stock" | Green text with check icon |
| In stock, quantity <= 10 | "Only X left in stock" | Amber text with warning icon |
| Out of stock, deny policy | "Out of stock" | Red text with X icon |
| Out of stock, continue policy | "Available on backorder" | Blue text with info icon |

Small text, icon + text inline. Wrapped in a live region for screen reader announcements on variant change.

#### Quantity Selector

Uses the quantity-selector component.

```
[-] [3] [+]
```

| Attribute | Description |
|---|---|
| Container | Inline flex, bordered, rounded |
| Decrease button | "-" icon, 40x40px. Disabled at minimum (1) |
| Input | Centered number input, 56px wide, 40px tall. Min = 1, Max = available stock (deny policy) or unlimited (continue policy) |
| Increase button | "+" icon, same size. Disabled at maximum |
| Disabled state | Reduced opacity, not-allowed cursor |
| Labels | Input labeled "Quantity", buttons labeled "Decrease quantity" / "Increase quantity" |

#### Add to Cart Button

| Attribute | Description |
|---|---|
| Width | Full width |
| Style | Primary button: blue background, white text, semi-bold, generous padding, rounded |
| Text | "Add to cart" |
| Loading state | Text changes to "Adding..." with spinner icon. Button disabled during loading |
| Sold out state | Gray background, muted text, not-allowed cursor. Text: "Sold out" |
| Spacing | Generous top margin |

**Livewire action (addToCart):**

1. Validates variant is selected and in stock
2. Adds to cart (creates cart if none exists in session)
3. Dispatches "cart-updated" browser event
4. Cart drawer opens automatically

#### Product Description

- Separated from above content by a horizontal rule with generous vertical spacing
- Rendered HTML styled with prose/typography styling

#### Tags

- Displayed below the description (if tags exist)
- Horizontal wrapping row with gap
- Each tag: rounded pill, gray background, small text

### 5.4 Accessibility Summary

| Requirement | Implementation |
|---|---|
| Variant radio buttons | Properly labeled with labels and grouped with fieldset/legend |
| Live regions | Price display, stock messaging, add-to-cart feedback |
| Image alt text | All images have meaningful alt text |


sed -n '508,675p' specs/08-PLAYWRIGHT-E2E-PLAN.md

## Suite 3: Admin Product Management

**File:** `tests/Browser/Admin/ProductManagementTest.php`

**Purpose:** Verify CRUD operations for products through the admin interface - listing, creating, editing, archiving, searching, and filtering by status.

### Product Lifecycle States

```mermaid
stateDiagram-v2
    [*] --> Draft: create product (default)
    Draft --> Active: set status to Active + Save
    Active --> Archived: set status to Archived + Save
    Draft --> Archived: set status to Archived + Save
    Archived --> Active: set status to Active + Save
    Active --> Draft: set status to Draft + Save
```

---

### Test 3.1: Shows the product list with seeded products

**Preconditions:** Logged in as admin, seeded products

**Steps:**
1. Navigate to `/admin/login`
2. Enter email: `admin@acme.test`
3. Enter password: `password`
4. Click the "Sign in" button
5. Click "Products" in the sidebar

**Expected Results:**
- Page displays "Classic Cotton T-Shirt"
- Page displays "Premium Slim Fit Jeans"
- No JavaScript errors

---

### Test 3.2: Can create a new product

**Preconditions:** Logged in as admin

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin (email: `admin@acme.test`, password: `password`)
3. Click "Products" in the sidebar
4. Click "Add product"
5. Enter title: `Test Product Created by E2E`
6. Enter description: `This product was created by the E2E test suite.`
7. Enter vendor: `Test Vendor`
8. Enter product type: `T-Shirts`
9. Enter variant price (variants.0.price): `29.99`
10. Enter variant SKU (variants.0.sku): `E2E-TEST-001`
11. Enter variant quantity (variants.0.quantity): `50`
12. Click "Save"
13. Click "Products" to return to the product list

**Expected Results:**
- After step 12: page displays "Product saved", no JavaScript errors
- After step 13: page displays "Test Product Created by E2E" in the list

---

### Test 3.3: Can edit an existing product title

**Preconditions:** Logged in as admin, seeded products

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Verify "Classic Cotton T-Shirt" is visible in the list
5. Click "Classic Cotton T-Shirt" to open the edit form
6. Clear the title field and enter: `Classic Cotton T-Shirt Updated`
7. Click "Save"
8. Click "Products" to return to the product list

**Expected Results:**
- After step 7: page displays "Product saved", no JavaScript errors
- After step 8: page displays "Classic Cotton T-Shirt Updated" in the list

---

### Test 3.4: Can archive a product

**Preconditions:** Logged in as admin

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Click "Add product"
5. Enter title: `Product To Archive`
6. Enter variant price: `19.99`
7. Enter variant SKU: `E2E-ARCHIVE-001`
8. Enter variant quantity: `10`
9. Click "Save"
10. Verify "Product saved" is displayed
11. Click "Products" to return to the product list
12. Click "Product To Archive" to open the edit form
13. Change status to "Archived" (click the Archived option)
14. Click "Save"
15. Click "Products" to return to the product list

**Expected Results:**
- After step 9: page displays "Product saved"
- After step 14: page displays "Product saved"
- After step 15: with default "Active" filter, the archived product should not appear in the list

---

### Test 3.5: Shows draft products only in admin, not storefront

**Preconditions:** Logged in as admin, Product #15 exists with draft status

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Verify Product #15 is visible in the admin list with a "Draft" badge
5. Open a new browser context / navigate to `/collections/t-shirts` (as guest)
6. Verify the draft product does NOT appear in the collection listing
7. Navigate to `/search?q=draft`
8. Verify the draft product does NOT appear in search results

**Expected Results:**
- Step 4: draft product visible in admin, no JavaScript errors
- Step 6: draft product absent from storefront collection, no JavaScript errors
- Step 8: draft product absent from search results, no JavaScript errors

---

### Test 3.6: Can search products in admin

**Preconditions:** Logged in as admin, seeded products

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Enter "Cotton" in the search input field

**Expected Results:**
- Page displays "Classic Cotton T-Shirt" in filtered results
- No JavaScript errors

---

### Test 3.7: Can filter products by status in admin

**Preconditions:** Logged in as admin, seeded products including drafts

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Click the "Draft" status filter tab
5. Verify only draft products are visible
6. Click the "Active" status filter tab

**Expected Results:**
- After step 4: only draft products are visible, no JavaScript errors
- After step 6: "Classic Cotton T-Shirt" is visible, no JavaScript errors

**Total: 7 tests**

---


sed -n '1185,1265p' specs/08-PLAYWRIGHT-E2E-PLAN.md

## Suite 7: Storefront Browsing

**File:** `tests/Browser/Storefront/BrowsingTest.php`

**Purpose:** Verify all storefront pages render correctly as a guest visitor - home page, collection listings, product detail with variant options, search results, static pages, and content visibility rules (draft/stock).

### Storefront Page Map

```mermaid
flowchart LR
    Home["/"] --> Collections["/collections"]
    Home --> Product["/products/{handle}"]
    Collections --> CollectionDetail["/collections/{handle}"]
    CollectionDetail --> Product
    Home --> Search["/search?q=..."]
    Search --> Product
    Home --> Pages["/pages/{handle}"]
    Product --> Cart["/cart"]
```

---

### Test 7.1: Shows featured products on home page

**Preconditions:** Seeded products

**Steps:**
1. Navigate to `/`

**Expected Results:**
- Page displays "Acme Fashion"
- Page displays "Classic Cotton T-Shirt"
- Page displays "24.99"
- No JavaScript errors

---

### Test 7.2: Shows collection with product grid

**Preconditions:** Seeded collections and products

**Steps:**
1. Navigate to `/collections/t-shirts`

**Expected Results:**
- Page displays "T-Shirts"
- Page displays "Classic Cotton T-Shirt"
- No JavaScript errors

---

### Test 7.3: Can navigate from collection to product

**Preconditions:** Seeded collections and products

**Steps:**
1. Navigate to `/collections/t-shirts`
2. Click "Classic Cotton T-Shirt"

**Expected Results:**
- Page displays "Classic Cotton T-Shirt"
- Page displays "24.99"
- Page displays "Add to cart"
- No JavaScript errors

---

### Test 7.4: Shows product detail with variant options

**Preconditions:** Product #1

**Steps:**
1. Navigate to `/products/classic-cotton-t-shirt`

**Expected Results:**
- Page displays "Classic Cotton T-Shirt"
- Page displays "24.99"
- Page displays "Size"
- Page displays "Color"
- No JavaScript errors


Output:
Chunk ID: e46369
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 467
Output:
## Suite 7: Storefront Browsing

**File:** `tests/Browser/Storefront/BrowsingTest.php`

**Purpose:** Verify all storefront pages render correctly as a guest visitor - home page, collection listings, product detail with variant options, search results, static pages, and content visibility rules (draft/stock).

### Storefront Page Map

```mermaid
flowchart LR
    Home["/"] --> Collections["/collections"]
    Home --> Product["/products/{handle}"]
    Collections --> CollectionDetail["/collections/{handle}"]
    CollectionDetail --> Product
    Home --> Search["/search?q=..."]
    Search --> Product
    Home --> Pages["/pages/{handle}"]
    Product --> Cart["/cart"]
```

---

### Test 7.1: Shows featured products on home page

**Preconditions:** Seeded products

**Steps:**
1. Navigate to `/`

**Expected Results:**
- Page displays "Acme Fashion"
- Page displays "Classic Cotton T-Shirt"
- Page displays "24.99"
- No JavaScript errors

---

### Test 7.2: Shows collection with product grid

**Preconditions:** Seeded collections and products

**Steps:**
1. Navigate to `/collections/t-shirts`

**Expected Results:**
- Page displays "T-Shirts"
- Page displays "Classic Cotton T-Shirt"
- No JavaScript errors

---

### Test 7.3: Can navigate from collection to product

**Preconditions:** Seeded collections and products

**Steps:**
1. Navigate to `/collections/t-shirts`
2. Click "Classic Cotton T-Shirt"

**Expected Results:**
- Page displays "Classic Cotton T-Shirt"
- Page displays "24.99"
- Page displays "Add to cart"
- No JavaScript errors

---

### Test 7.4: Shows product detail with variant options

**Preconditions:** Product #1

**Steps:**
1. Navigate to `/products/classic-cotton-t-shirt`

**Expected Results:**
- Page displays "Classic Cotton T-Shirt"
- Page displays "24.99"
- Page displays "Size"
- Page displays "Color"
- No JavaScript errors


Output:
Chunk ID: eb23a5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1256
Output:
## Suite 3: Admin Product Management

**File:** `tests/Browser/Admin/ProductManagementTest.php`

**Purpose:** Verify CRUD operations for products through the admin interface - listing, creating, editing, archiving, searching, and filtering by status.

### Product Lifecycle States

```mermaid
stateDiagram-v2
    [*] --> Draft: create product (default)
    Draft --> Active: set status to Active + Save
    Active --> Archived: set status to Archived + Save
    Draft --> Archived: set status to Archived + Save
    Archived --> Active: set status to Active + Save
    Active --> Draft: set status to Draft + Save
```

---

### Test 3.1: Shows the product list with seeded products

**Preconditions:** Logged in as admin, seeded products

**Steps:**
1. Navigate to `/admin/login`
2. Enter email: `admin@acme.test`
3. Enter password: `password`
4. Click the "Sign in" button
5. Click "Products" in the sidebar

**Expected Results:**
- Page displays "Classic Cotton T-Shirt"
- Page displays "Premium Slim Fit Jeans"
- No JavaScript errors

---

### Test 3.2: Can create a new product

**Preconditions:** Logged in as admin

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin (email: `admin@acme.test`, password: `password`)
3. Click "Products" in the sidebar
4. Click "Add product"
5. Enter title: `Test Product Created by E2E`
6. Enter description: `This product was created by the E2E test suite.`
7. Enter vendor: `Test Vendor`
8. Enter product type: `T-Shirts`
9. Enter variant price (variants.0.price): `29.99`
10. Enter variant SKU (variants.0.sku): `E2E-TEST-001`
11. Enter variant quantity (variants.0.quantity): `50`
12. Click "Save"
13. Click "Products" to return to the product list

**Expected Results:**
- After step 12: page displays "Product saved", no JavaScript errors
- After step 13: page displays "Test Product Created by E2E" in the list

---

### Test 3.3: Can edit an existing product title

**Preconditions:** Logged in as admin, seeded products

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Verify "Classic Cotton T-Shirt" is visible in the list
5. Click "Classic Cotton T-Shirt" to open the edit form
6. Clear the title field and enter: `Classic Cotton T-Shirt Updated`
7. Click "Save"
8. Click "Products" to return to the product list

**Expected Results:**
- After step 7: page displays "Product saved", no JavaScript errors
- After step 8: page displays "Classic Cotton T-Shirt Updated" in the list

---

### Test 3.4: Can archive a product

**Preconditions:** Logged in as admin

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Click "Add product"
5. Enter title: `Product To Archive`
6. Enter variant price: `19.99`
7. Enter variant SKU: `E2E-ARCHIVE-001`
8. Enter variant quantity: `10`
9. Click "Save"
10. Verify "Product saved" is displayed
11. Click "Products" to return to the product list
12. Click "Product To Archive" to open the edit form
13. Change status to "Archived" (click the Archived option)
14. Click "Save"
15. Click "Products" to return to the product list

**Expected Results:**
- After step 9: page displays "Product saved"
- After step 14: page displays "Product saved"
- After step 15: with default "Active" filter, the archived product should not appear in the list

---

### Test 3.5: Shows draft products only in admin, not storefront

**Preconditions:** Logged in as admin, Product #15 exists with draft status

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Verify Product #15 is visible in the admin list with a "Draft" badge
5. Open a new browser context / navigate to `/collections/t-shirts` (as guest)
6. Verify the draft product does NOT appear in the collection listing
7. Navigate to `/search?q=draft`
8. Verify the draft product does NOT appear in search results

**Expected Results:**
- Step 4: draft product visible in admin, no JavaScript errors
- Step 6: draft product absent from storefront collection, no JavaScript errors
- Step 8: draft product absent from search results, no JavaScript errors

---

### Test 3.6: Can search products in admin

**Preconditions:** Logged in as admin, seeded products

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Enter "Cotton" in the search input field

**Expected Results:**
- Page displays "Classic Cotton T-Shirt" in filtered results
- No JavaScript errors

---

### Test 3.7: Can filter products by status in admin

**Preconditions:** Logged in as admin, seeded products including drafts

**Steps:**
1. Navigate to `/admin/login`
2. Log in as admin
3. Click "Products" in the sidebar
4. Click the "Draft" status filter tab
5. Verify only draft products are visible
6. Click the "Active" status filter tab

**Expected Results:**
- After step 4: only draft products are visible, no JavaScript errors
- After step 6: "Classic Cotton T-Shirt" is visible, no JavaScript errors

**Total: 7 tests**

---


You are a read-only QA/spec explorer. For the next Phase 2 UI slice, inspect specs/02-API-ROUTES.md, specs/03-ADMIN-UI.md, specs/04-STOREFRONT-UI.md, and specs/08-PLAYWRIGHT-E2E-PLAN.md. Return a concise checklist of the minimum admin catalog and storefront browsing routes/components/tests needed before we can honestly mark Phase 2 catalog UI as implemented. Do not edit files.


Output: Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.


You are a read-only QA/spec explorer in /Users/fabianwesner/Herd/shop. For the next Phase 2 UI slice, inspect specs/02-API-ROUTES.md, specs/03-ADMIN-UI.md, specs/04-STOREFRONT-UI.md, and specs/08-PLAYWRIGHT-E2E-PLAN.md. Return a concise checklist of the minimum admin catalog and storefront browsing routes/components/tests needed before we can honestly mark Phase 2 catalog UI as implemented. Do not edit files.


Prompt: You are a read-only QA/spec explorer in /Users/fabianwesner/Herd/shop. For the next Phase 2 UI slice, inspect specs/02-API-ROUTES.md, specs/03-ADMIN-UI.md, specs/04-STOREFRONT-UI.md, and specs/08-PLAYWRIGHT-E2E-PLAN.md. Return a concise checklist of the minimum admin catalog and storefront browsing routes/components/tests needed before we can honestly mark Phase 2 catalog UI as implemented. Do not edit files.


Output:
Chunk ID: 9c9dae
Wall time: 0.1428 seconds
Process exited with code 0
Original token count: 319
Output:
Description:
  Create a new Livewire component

Usage:
  make:livewire [options] [--] [<name>]

Arguments:
  name                  The name of the component

Options:
      --sfc             Create a single-file component
      --mfc             Create a multi-file component
      --class           Create a class-based component
      --type=TYPE       Component type (sfc, mfc, or class)
      --test            Create a test file
      --emoji=EMOJI     Use emoji in file/directory names (true or false)
      --js              Create a JavaScript file for multi-file components
      --css             Create CSS files for multi-file components
  -h, --help            Display help for the given command. When no command is given display help for the list command
      --silent          Do not output any message
  -q, --quiet           Only errors are displayed. All other output is suppressed
  -V, --version         Display this application version
      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug


/bin/zsh
-lc
php artisan make:livewire --help

Description:
  Create a new Livewire component

Usage:
  make:livewire [options] [--] [<name>]

Arguments:
  name                  The name of the component

Options:
      --sfc             Create a single-file component
      --mfc             Create a multi-file component
      --class           Create a class-based component
      --type=TYPE       Component type (sfc, mfc, or class)
      --test            Create a test file
      --emoji=EMOJI     Use emoji in file/directory names (true or false)
      --js              Create a JavaScript file for multi-file components
      --css             Create CSS files for multi-file components
  -h, --help            Display help for the given command. When no command is given display help for the list command
      --silent          Do not output any message
  -q, --quiet           Only errors are displayed. All other output is suppressed
  -V, --version         Display this application version
      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug


I’ll use class-based Livewire components because the existing app uses that convention. The first pass will cover the real catalog routes and workflows that the E2E plan expects: product list/create/edit/archive/search/filter, collection list/create/edit, and storefront home/collections/products/search browsing.


php artisan make:livewire Admin/Products/Index --class --no-interaction && php artisan make:livewire Admin/Products/Form --class --no-interaction && php artisan make:livewire Admin/Collections/Index --class --no-interaction && php artisan make:livewire Admin/Collections/Form --class --no-interaction && php artisan make:livewire Storefront/Home --class --no-interaction && php artisan make:livewire Storefront/Collections/Index --class --no-interaction && php artisan make:livewire Storefront/Collections/Show --class --no-interaction && php artisan make:livewire Storefront/Products/Show --class --no-interaction && php artisan make:livewire Storefront/Search/Index --class --no-interaction


Output:
Chunk ID: 173458
Wall time: 1.2361 seconds
Process exited with code 0
Original token count: 217
Output:

   INFO  Livewire component [app/Livewire/Admin/Products/Index.php] created successfully.

   INFO  Livewire component [app/Livewire/Admin/Products/Form.php] created successfully.

   INFO  Livewire component [app/Livewire/Admin/Collections/Index.php] created successfully.

   INFO  Livewire component [app/Livewire/Admin/Collections/Form.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Home.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Collections/Index.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Collections/Show.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Products/Show.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Search/Index.php] created successfully.


php artisan make:livewire Admin/Products/Index --class --no-interaction && php artisan make:livewire Admin/Products/Form --class --no-interaction && php artisan make:livewire Admin/Collections/Index --class --no-interaction && php artisan make:livewire Admin/Collections/Form --class --no-interaction && php artisan make:livewire Storefront/Home --class --no-interaction && php artisan make:livewire Storefront/Collections/Index --class --no-interaction && php artisan make:livewire Storefront/Collections/Show --class --no-interaction && php artisan make:livewire Storefront/Products/Show --class --no-interaction && php artisan make:livewire Storefront/Search/Index --class --no-interaction

   INFO  Livewire component [app/Livewire/Admin/Products/Index.php] created successfully.

   INFO  Livewire component [app/Livewire/Admin/Products/Form.php] created successfully.

   INFO  Livewire component [app/Livewire/Admin/Collections/Index.php] created successfully.

   INFO  Livewire component [app/Livewire/Admin/Collections/Form.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Home.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Collections/Index.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Collections/Show.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Products/Show.php] created successfully.

   INFO  Livewire component [app/Livewire/Storefront/Search/Index.php] created successfully.


find app/Livewire/Admin app/Livewire/Storefront -type f | sort


find resources/views/livewire/admin resources/views/livewire/storefront -type f | sort


sed -n '1,120p' app/Livewire/Admin/Products/Index.php


sed -n '1,80p' resources/views/livewire/admin/products/index.blade.php


sed -n '1,120p' app/Livewire/Admin/Products/Index.php

<?php

namespace App\Livewire\Admin\Products;

use Livewire\Component;

class Index extends Component
{
    public function render()
    {
        return view('livewire.admin.products.index');
    }
}


sed -n '1,80p' resources/views/livewire/admin/products/index.blade.php

{{-- He who is contented is rich. - Laozi --}}


Output:
Chunk ID: edafd3
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 124
Output:
app/Livewire/Admin/Auth/Login.php
app/Livewire/Admin/Collections/Form.php
app/Livewire/Admin/Collections/Index.php
app/Livewire/Admin/Products/Form.php
app/Livewire/Admin/Products/Index.php
app/Livewire/Storefront/Account/Auth/Login.php
app/Livewire/Storefront/Account/Auth/Register.php
app/Livewire/Storefront/Collections/Index.php
app/Livewire/Storefront/Collections/Show.php
app/Livewire/Storefront/Home.php
app/Livewire/Storefront/Products/Show.php
app/Livewire/Storefront/Search/Index.php


Output:
Chunk ID: a44236
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 178
Output:
resources/views/livewire/admin/auth/login.blade.php
resources/views/livewire/admin/collections/form.blade.php
resources/views/livewire/admin/collections/index.blade.php
resources/views/livewire/admin/products/form.blade.php
resources/views/livewire/admin/products/index.blade.php
resources/views/livewire/storefront/account/auth/login.blade.php
resources/views/livewire/storefront/account/auth/register.blade.php
resources/views/livewire/storefront/collections/index.blade.php
resources/views/livewire/storefront/collections/show.blade.php
resources/views/livewire/storefront/home.blade.php
resources/views/livewire/storefront/products/show.blade.php
resources/views/livewire/storefront/search/index.blade.php


Output:
Chunk ID: 71990e
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 51
Output:
<?php

namespace App\Livewire\Admin\Products;

use Livewire\Component;

class Index extends Component
{
    public function render()
    {
        return view('livewire.admin.products.index');
    }
}


Output:
Chunk ID: 94c331
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 16
Output:
<div>
    {{-- He who is contented is rich. - Laozi --}}
</div>


sed -n '1,240p' resources/views/partials/head.blade.php


sed -n '1,260p' resources/views/welcome.blade.php


sed -n '1,220p' resources/views/layouts/auth.blade.php


sed -n '1,240p' resources/views/partials/head.blade.php

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>{{ $title ?? config('app.name') }}</title>

<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">

<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />

@vite(['resources/css/app.css', 'resources/js/app.js'])
@fluxAppearance


Output:
Chunk ID: e658ff
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 136
Output:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>{{ $title ?? config('app.name') }}</title>

<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">

<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />

@vite(['resources/css/app.css', 'resources/js/app.js'])
@fluxAppearance


/bin/zsh
-lc
sed -n '1,260p' routes/settings.php

<?php

use App\Livewire\Settings\Appearance;
use App\Livewire\Settings\Password;
use App\Livewire\Settings\Profile;
use App\Livewire\Settings\TwoFactor;
use Illuminate\Support\Facades\Route;
use Laravel\Fortify\Features;

Route::middleware(['auth'])->group(function () {
    Route::redirect('settings', 'settings/profile');

    Route::livewire('settings/profile', Profile::class)->name('profile.edit');
});

Route::middleware(['auth', 'verified'])->group(function () {
    Route::livewire('settings/password', Password::class)->name('user-password.edit');
    Route::livewire('settings/appearance', Appearance::class)->name('appearance.edit');

    Route::livewire('settings/two-factor', TwoFactor::class)
        ->middleware(
            when(
                Features::canManageTwoFactorAuthentication()
                && Features::optionEnabled(Features::twoFactorAuthentication(), 'confirmPassword'),
                ['password.confirm'],
                [],
            ),
        )
        ->name('two-factor.show');
});


sed -n '1,220p' resources/views/layouts/auth.blade.php

<x-layouts::auth.simple :title="$title ?? null">
    {{ $slot }}
</x-layouts::auth.simple>


sed -n '1,260p' resources/views/welcome.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <link rel="icon" href="/favicon.ico" sizes="any">
        <link rel="icon" href="/favicon.svg" type="image/svg+xml">
        <link rel="apple-touch-icon" href="/apple-touch-icon.png">

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />

        <!-- Styles -->
        <style>
            /*! tailwindcss v4.0.14 | MIT License | https://tailwindcss.com */
            @layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-green-600:oklch(.627 .194 149.214);--color-gray-900:oklch(.21 .034 264.665);--color-zinc-50:oklch(.985 0 0);--color-zinc-200:oklch(.92 .004 286.32);--color-zinc-400:oklch(.705 .015 286.067);--color-zinc-500:oklch(.552 .016 285.938);--color-zinc-600:oklch(.442 .017 285.786);--color-zinc-700:oklch(.37 .013 285.805);--color-zinc-800:oklch(.274 .006 286.033);--color-zinc-900:oklch(.21 .006 285.885);--color-neutral-100:oklch(.97 0 0);--color-neutral-200:oklch(.922 0 0);--color-neutral-700:oklch(.371 0 0);--color-neutral-800:oklch(.269 0 0);--color-neutral-900:oklch(.205 0 0);--color-neutral-950:oklch(.145 0 0);--color-stone-800:oklch(.268 .007 34.298);--color-stone-950:oklch(.147 .004 49.25);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--leading-tight:1.25;--leading-normal:1.5;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--aspect-video:16/9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-font-feature-settings:var(--font-sans--font-feature-settings);--default-font-variation-settings:var(--font-sans--font-variation-settings);--default-mono-font-family:var(--font-mono);--default-mono-font-feature-settings:var(--font-mono--font-feature-settings);--default-mono-font-variation-settings:var(--font-mono--font-variation-settings)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:…66604 chars truncated… 233.154L245.211 132.577Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M318.094 154.945C322.842 163.17 324.002 171.107 321.573 178.757L263.505 279.334C265.934 271.684 264.774 263.746 260.026 255.522L318.094 154.945Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M176.925 96.6737C180.127 91.1249 184.776 86.4503 190.871 82.6499L132.803 183.227C126.708 187.027 122.059 191.702 118.857 197.25L176.925 96.6737Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M387.226 217.606C385.989 219.749 384.59 221.813 383.028 223.797L324.96 324.373C326.522 322.39 327.921 320.326 329.157 318.183L387.226 217.606Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M317.269 188.408C319.087 185.262 320.519 182.045 321.562 178.758L263.494 279.335C262.451 282.622 261.019 285.839 259.201 288.985L317.269 188.408Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M245.208 132.573C241.895 137.928 243 145.387 248.522 154.95L190.454 255.527C184.932 245.964 183.827 238.505 187.14 233.15L245.208 132.573Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M176.93 96.6719C174.331 101.175 172.686 106.255 171.993 111.91L113.925 212.487C114.618 206.831 116.263 201.752 118.862 197.249L176.93 96.6719Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M317.266 188.413C314.853 192.589 311.757 196.64 307.978 200.566L249.91 301.143C253.689 297.216 256.785 293.166 259.198 288.99L317.266 188.413Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M464.198 304.708L435.375 254.789L377.307 355.366L406.13 405.285L464.198 304.708Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M353.209 254.787C366.68 242.548 376.618 232.22 383.023 223.805L324.955 324.382C318.55 332.797 308.612 343.124 295.141 355.364L353.209 254.787Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M435.37 254.787L353.212 254.784L295.144 355.361L377.302 355.364L435.37 254.787Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M183.921 154.947L248.521 154.95L190.453 255.527L125.853 255.524L183.921 154.947Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M171.992 111.914C170.668 124.537 174.643 138.881 183.92 154.947L125.852 255.524C116.575 239.458 112.599 225.114 113.924 212.491L171.992 111.914Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M307.987 200.562C301.251 207.256 291.203 216.244 277.842 227.528L219.774 328.105C233.135 316.821 243.183 307.832 249.919 301.139L307.987 200.562Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M15.5469 75.1797L44.5359 125.386L-13.5321 225.963L-42.5212 175.756L15.5469 75.1797Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M277.836 227.536C264.033 238.82 253.708 247.904 246.862 254.789L188.794 355.366C195.64 348.481 205.965 339.397 219.768 328.113L277.836 227.536Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M275.358 304.706L464.189 304.713L406.12 405.29L217.29 405.283L275.358 304.706Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M44.5279 125.39L67.3864 125.39L9.31834 225.967L-13.5401 225.966L44.5279 125.39Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M101.341 75.1911L233.863 304.705L175.795 405.282L43.2733 175.768L101.341 75.1911ZM15.5431 75.19L-42.525 175.767L43.277 175.77L101.345 75.1932L15.5431 75.19Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M246.866 254.784L246.534 254.784L188.466 355.361L188.798 355.361L246.866 254.784Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M246.539 254.781L275.362 304.701L217.294 405.277L188.471 355.358L246.539 254.781Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M67.3906 125.391L170.923 304.698L112.855 405.275L9.32257 225.967L67.3906 125.391Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                            <path d="M170.921 304.699L233.865 304.701L175.797 405.278L112.853 405.276L170.921 304.699Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
                        </g>
                        <g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4" style="mix-blend-mode:hard-light">