create-nymbal-app 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +14 -12
  3. package/dist/index.js.map +1 -1
  4. package/package.json +5 -4
  5. package/scaffold/project/astro/src/pages/index.astro +13 -15
  6. package/scaffold/templates/astro/_dot.node-version +1 -1
  7. package/scaffold/templates/astro/astro.config.mjs +23 -0
  8. package/scaffold/templates/astro/package.json +26 -27
  9. package/scaffold/templates/astro/src/components/SkipNav.astro +3 -1
  10. package/scaffold/templates/astro/src/elements/index.ts +11 -0
  11. package/scaffold/templates/astro/src/layouts/BaseLayout.astro +15 -4
  12. package/scaffold/templates/astro/src/lib/api.ts +126 -20
  13. package/scaffold/templates/astro/src/lib/chrome.ts +59 -0
  14. package/scaffold/templates/astro/src/lib/client.ts +4 -2
  15. package/scaffold/templates/astro/src/lib/fragments.ts +48 -0
  16. package/scaffold/templates/astro/src/lib/server-elements.ts +5 -0
  17. package/scaffold/templates/astro/src/middleware.ts +37 -0
  18. package/scaffold/templates/astro/src/pages/account/addresses.astro +5 -97
  19. package/scaffold/templates/astro/src/pages/account/login.astro +7 -34
  20. package/scaffold/templates/astro/src/pages/account/profile.astro +6 -56
  21. package/scaffold/templates/astro/src/pages/account/register.astro +7 -73
  22. package/scaffold/templates/astro/src/pages/cart.astro +14 -7
  23. package/scaffold/templates/astro/src/pages/category/[slug].astro +41 -26
  24. package/scaffold/templates/astro/src/pages/checkout.astro +34 -90
  25. package/scaffold/templates/astro/src/pages/fragments/footer.astro +5 -2
  26. package/scaffold/templates/astro/src/pages/fragments/header.astro +12 -2
  27. package/scaffold/templates/astro/src/pages/fragments/nav.astro +8 -5
  28. package/scaffold/templates/astro/src/pages/fragments/product/[slug].astro +9 -10
  29. package/scaffold/templates/astro/src/pages/index.astro +18 -15
  30. package/scaffold/templates/astro/src/pages/order/[orderNumber].astro +22 -44
  31. package/scaffold/templates/astro/src/pages/product/[slug].astro +46 -29
  32. package/scaffold/templates/astro/src/pages/products/index.astro +40 -26
  33. package/scaffold/templates/astro/src/pages/search.astro +13 -8
  34. package/scaffold/templates/astro/src/scripts/init-client.ts +14 -0
  35. package/scaffold/templates/astro/src/scripts/pdp.ts +47 -53
  36. package/scaffold/templates/astro/src/scripts/plp.ts +21 -98
  37. package/scaffold/templates/astro/src/scripts/search.ts +19 -120
  38. package/scaffold/templates/astro/src/scripts/signed-in-redirect.ts +4 -0
  39. package/scaffold/templates/astro/src/styles/global.css +49 -45
  40. package/scaffold/templates/astro/src/styles/pages/account.css +0 -90
  41. package/scaffold/templates/astro/src/styles/pages/confirmation.css +6 -0
  42. package/scaffold/templates/astro/src/styles/pages/home.css +9 -100
  43. package/scaffold/templates/astro/src/styles/pages/pdp.css +5 -167
  44. package/scaffold/templates/astro/src/styles/pages/plp.css +3 -42
  45. package/scaffold/templates/astro/src/styles/pages/search.css +13 -100
  46. package/scaffold/templates/astro/src/styles/tokens.css +56 -0
  47. package/scaffold/templates/nextjs/_dot.node-version +1 -1
  48. package/scaffold/templates/nextjs/app/(catalog)/category/[slug]/page.tsx +74 -0
  49. package/scaffold/templates/nextjs/app/(catalog)/home/[key]/page.tsx +80 -0
  50. package/scaffold/templates/nextjs/app/(catalog)/layout.tsx +13 -0
  51. package/scaffold/templates/nextjs/app/(catalog)/not-found.tsx +7 -0
  52. package/scaffold/templates/nextjs/app/(catalog)/product/[slug]/page.tsx +182 -0
  53. package/scaffold/templates/nextjs/app/(catalog)/products/page.tsx +62 -0
  54. package/scaffold/templates/nextjs/app/(catalog)/search/SearchView.tsx +90 -0
  55. package/scaffold/templates/nextjs/app/(catalog)/search/page.tsx +46 -0
  56. package/scaffold/templates/nextjs/app/(shopper)/account/addresses/AddressesView.tsx +5 -0
  57. package/scaffold/templates/nextjs/app/(shopper)/account/addresses/page.tsx +17 -0
  58. package/scaffold/templates/nextjs/app/(shopper)/account/login/LoginView.tsx +28 -0
  59. package/scaffold/templates/nextjs/app/(shopper)/account/login/page.tsx +23 -0
  60. package/scaffold/templates/nextjs/app/(shopper)/account/profile/ProfileView.tsx +5 -0
  61. package/scaffold/templates/nextjs/app/(shopper)/account/profile/page.tsx +20 -0
  62. package/scaffold/templates/nextjs/app/(shopper)/account/register/RegisterView.tsx +26 -0
  63. package/scaffold/templates/nextjs/app/(shopper)/account/register/page.tsx +11 -0
  64. package/scaffold/templates/nextjs/app/(shopper)/cart/CartView.tsx +5 -0
  65. package/scaffold/templates/nextjs/app/(shopper)/cart/page.tsx +22 -0
  66. package/scaffold/templates/nextjs/app/(shopper)/checkout/CheckoutView.tsx +27 -0
  67. package/scaffold/templates/nextjs/app/(shopper)/checkout/page.tsx +39 -0
  68. package/scaffold/templates/nextjs/app/(shopper)/layout.tsx +12 -0
  69. package/scaffold/templates/nextjs/app/{order → (shopper)/order}/[orderNumber]/page.tsx +24 -5
  70. package/scaffold/templates/nextjs/app/error.tsx +3 -7
  71. package/scaffold/templates/nextjs/app/fragments/footer/route.ts +13 -0
  72. package/scaffold/templates/nextjs/app/fragments/header/[key]/route.ts +18 -0
  73. package/scaffold/templates/nextjs/app/fragments/nav/[key]/route.ts +23 -0
  74. package/scaffold/templates/nextjs/app/fragments/product/[slug]/route.ts +22 -0
  75. package/scaffold/templates/nextjs/app/globals.css +5 -23
  76. package/scaffold/templates/nextjs/app/layout.tsx +8 -5
  77. package/scaffold/templates/nextjs/app/not-found.tsx +9 -33
  78. package/scaffold/templates/nextjs/app/tokens.css +56 -0
  79. package/scaffold/templates/nextjs/components/client/ListingControls.tsx +87 -0
  80. package/scaffold/templates/nextjs/components/client/NymbalSetup.tsx +48 -6
  81. package/scaffold/templates/nextjs/components/client/ProductInteractive.tsx +64 -28
  82. package/scaffold/templates/nextjs/components/client/elements.tsx +15 -0
  83. package/scaffold/templates/nextjs/components/server/Header.tsx +33 -97
  84. package/scaffold/templates/nextjs/components/server/NotFoundView.tsx +39 -0
  85. package/scaffold/templates/nextjs/components/server/SkipNav.tsx +7 -3
  86. package/scaffold/templates/nextjs/elements/index.ts +12 -0
  87. package/scaffold/templates/nextjs/lib/api.ts +162 -46
  88. package/scaffold/templates/nextjs/lib/chrome.ts +40 -0
  89. package/scaffold/templates/nextjs/lib/client.ts +3 -0
  90. package/scaffold/templates/nextjs/lib/fragments.ts +16 -0
  91. package/scaffold/templates/nextjs/lib/header.ts +39 -0
  92. package/scaffold/templates/nextjs/lib/render.ts +14 -0
  93. package/scaffold/templates/nextjs/next.config.mjs +32 -0
  94. package/scaffold/templates/nextjs/package.json +28 -29
  95. package/scaffold/templates/nextjs/styles/pages/account.module.css +0 -70
  96. package/scaffold/templates/nextjs/styles/pages/confirmation.module.css +6 -85
  97. package/scaffold/templates/nextjs/styles/pages/home.module.css +2 -103
  98. package/scaffold/templates/nextjs/styles/pages/pdp.module.css +3 -179
  99. package/scaffold/templates/nextjs/styles/pages/plp.module.css +8 -141
  100. package/scaffold/templates/nextjs/styles/pages/search.module.css +8 -128
  101. package/scaffold/templates/nextjs/tsconfig.dist.json +5 -0
  102. package/scaffold/templates/astro/src/components/AddressForm.astro +0 -107
  103. package/scaffold/templates/astro/src/components/Breadcrumb.astro +0 -20
  104. package/scaffold/templates/astro/src/components/CategoryNav.astro +0 -27
  105. package/scaffold/templates/astro/src/components/CheckoutSteps.astro +0 -29
  106. package/scaffold/templates/astro/src/components/Footer.astro +0 -108
  107. package/scaffold/templates/astro/src/components/Header.astro +0 -192
  108. package/scaffold/templates/astro/src/components/ImageGallery.astro +0 -42
  109. package/scaffold/templates/astro/src/components/OrderSummary.astro +0 -53
  110. package/scaffold/templates/astro/src/components/Pagination.astro +0 -13
  111. package/scaffold/templates/astro/src/components/PriceDisplay.astro +0 -25
  112. package/scaffold/templates/astro/src/components/ProductCard.astro +0 -44
  113. package/scaffold/templates/astro/src/components/RelatedProducts.astro +0 -28
  114. package/scaffold/templates/astro/src/components/ReviewForm.astro +0 -74
  115. package/scaffold/templates/astro/src/components/ReviewList.astro +0 -41
  116. package/scaffold/templates/astro/src/components/SortDropdown.astro +0 -26
  117. package/scaffold/templates/astro/src/components/StockBadge.astro +0 -32
  118. package/scaffold/templates/astro/src/scripts/auth-forms.ts +0 -82
  119. package/scaffold/templates/astro/src/scripts/cart-page.ts +0 -143
  120. package/scaffold/templates/astro/src/scripts/checkout.ts +0 -272
  121. package/scaffold/templates/astro/src/scripts/image-gallery.ts +0 -22
  122. package/scaffold/templates/astro/src/scripts/profile-form.ts +0 -57
  123. package/scaffold/templates/astro/src/scripts/review-form.ts +0 -27
  124. package/scaffold/templates/astro/src/styles/pages/cart.css +0 -243
  125. package/scaffold/templates/astro/src/styles/pages/checkout.css +0 -270
  126. package/scaffold/templates/nextjs/app/account/addresses/page.tsx +0 -10
  127. package/scaffold/templates/nextjs/app/account/login/page.tsx +0 -23
  128. package/scaffold/templates/nextjs/app/account/profile/page.tsx +0 -10
  129. package/scaffold/templates/nextjs/app/account/register/page.tsx +0 -10
  130. package/scaffold/templates/nextjs/app/cart/page.tsx +0 -10
  131. package/scaffold/templates/nextjs/app/category/[slug]/page.tsx +0 -58
  132. package/scaffold/templates/nextjs/app/checkout/page.tsx +0 -10
  133. package/scaffold/templates/nextjs/app/page.tsx +0 -63
  134. package/scaffold/templates/nextjs/app/product/[slug]/page.tsx +0 -111
  135. package/scaffold/templates/nextjs/app/products/page.tsx +0 -40
  136. package/scaffold/templates/nextjs/app/search/page.tsx +0 -24
  137. package/scaffold/templates/nextjs/components/client/AddressManager.tsx +0 -181
  138. package/scaffold/templates/nextjs/components/client/CartPage.tsx +0 -120
  139. package/scaffold/templates/nextjs/components/client/CheckoutForm.tsx +0 -523
  140. package/scaffold/templates/nextjs/components/client/ImageGallery.tsx +0 -68
  141. package/scaffold/templates/nextjs/components/client/LoginForm.tsx +0 -89
  142. package/scaffold/templates/nextjs/components/client/MiniCartWrapper.tsx +0 -12
  143. package/scaffold/templates/nextjs/components/client/ProductGrid.tsx +0 -148
  144. package/scaffold/templates/nextjs/components/client/ProfileForm.tsx +0 -112
  145. package/scaffold/templates/nextjs/components/client/RegisterForm.tsx +0 -131
  146. package/scaffold/templates/nextjs/components/client/ReviewForm.tsx +0 -116
  147. package/scaffold/templates/nextjs/components/client/SearchBarWrapper.tsx +0 -27
  148. package/scaffold/templates/nextjs/components/client/SearchResults.tsx +0 -141
  149. package/scaffold/templates/nextjs/components/client/SortDropdown.tsx +0 -35
  150. package/scaffold/templates/nextjs/components/server/Breadcrumb.tsx +0 -36
  151. package/scaffold/templates/nextjs/components/server/CategoryNav.tsx +0 -29
  152. package/scaffold/templates/nextjs/components/server/CheckoutSteps.tsx +0 -60
  153. package/scaffold/templates/nextjs/components/server/Footer.module.css +0 -64
  154. package/scaffold/templates/nextjs/components/server/Footer.tsx +0 -79
  155. package/scaffold/templates/nextjs/components/server/Header.module.css +0 -147
  156. package/scaffold/templates/nextjs/components/server/OrderSummary.tsx +0 -79
  157. package/scaffold/templates/nextjs/components/server/PriceDisplay.tsx +0 -31
  158. package/scaffold/templates/nextjs/components/server/ProductCard.tsx +0 -59
  159. package/scaffold/templates/nextjs/components/server/RelatedProducts.tsx +0 -31
  160. package/scaffold/templates/nextjs/components/server/ReviewList.tsx +0 -62
  161. package/scaffold/templates/nextjs/components/server/StockBadge.tsx +0 -19
  162. package/scaffold/templates/nextjs/styles/pages/cart.module.css +0 -240
  163. package/scaffold/templates/nextjs/styles/pages/checkout.module.css +0 -404
  164. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/layout.tsx +0 -0
  165. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/orders/[orderNumber]/page.tsx +0 -0
  166. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/orders/page.tsx +0 -0
  167. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/page.tsx +0 -0
@@ -1,145 +1,44 @@
1
+ import { NymbalProductFilter } from '@nymbalhq/web-components/elements/nymbal-product-filter'
2
+ import { NymbalSearchResults } from '@nymbalhq/web-components/elements/nymbal-search-results'
1
3
  import { client } from '../lib/client.js'
2
- import { formatPrice } from '../lib/format.js'
3
4
 
4
- const resultsContainer = document.getElementById('search-results')
5
- const queryLabel = document.getElementById('search-query-label')
6
- const searchInput = document.getElementById('search-input') as HTMLInputElement | null
7
-
8
- /** Builds an element; dynamic values go in through setAttribute and text nodes, never through markup. */
9
- function el(
10
- tag: string,
11
- attrs: Record<string, string> = {},
12
- ...children: (Node | string)[]
13
- ): HTMLElement {
14
- const node = document.createElement(tag)
15
- for (const [name, value] of Object.entries(attrs)) node.setAttribute(name, value)
16
- node.append(...children)
17
- return node
18
- }
19
-
20
- function renderProductCard(product: any): HTMLElement {
21
- const slug = String(product.slug ?? '')
22
- const name = String(product.name ?? '')
23
- const imageUrl =
24
- product.imageUrl ??
25
- (Array.isArray(product.media) && product.media[0]?.url) ??
26
- `https://picsum.photos/seed/${encodeURIComponent(slug)}/600/600`
27
- const price =
28
- product.priceMinor != null ? formatPrice(product.priceMinor, product.currency ?? 'GBP') : ''
29
-
30
- const body = el(
31
- 'div',
32
- { class: 'product-card-body' },
33
- el('span', { class: 'product-card-name' }, name),
34
- )
35
- if (price) body.append(el('span', { class: 'product-card-price' }, price))
36
-
37
- return el(
38
- 'a',
39
- {
40
- href: `/product/${encodeURIComponent(slug)}`,
41
- class: 'product-card',
42
- 'data-testid': `product-card-${slug}`,
43
- },
44
- el('img', {
45
- src: String(imageUrl),
46
- alt: name,
47
- class: 'product-card-image',
48
- loading: 'lazy',
49
- width: '600',
50
- height: '600',
51
- }),
52
- body,
53
- )
54
- }
55
-
56
- function renderResults() {
57
- const state = client.search.getState()
58
- const results = state.results ?? []
59
- const query = state.query ?? ''
60
- const isLoading = state.loading ?? false
61
-
62
- if (!resultsContainer) return
63
-
64
- // Update query label
65
- if (queryLabel) {
66
- if (query) {
67
- queryLabel.replaceChildren('Results for ', el('strong', {}, `"${query}"`))
68
- queryLabel.style.display = ''
69
- } else {
70
- queryLabel.style.display = 'none'
71
- }
72
- }
5
+ NymbalProductFilter.define()
6
+ NymbalSearchResults.define()
73
7
 
74
- if (isLoading) {
75
- resultsContainer.innerHTML = `
76
- <div class="search-loading">Searching...</div>
77
- `
78
- return
79
- }
80
-
81
- if (!query) {
82
- resultsContainer.innerHTML = `
83
- <div class="search-empty">
84
- <div class="search-empty-icon" aria-hidden="true">&#128269;</div>
85
- <h2 class="search-empty-title">Search our store</h2>
86
- <p class="search-empty-text">Type a keyword to find products.</p>
87
- </div>
88
- `
89
- return
90
- }
91
-
92
- if (results.length === 0) {
93
- resultsContainer.innerHTML = `
94
- <div class="search-empty">
95
- <div class="search-empty-icon" aria-hidden="true">&#128269;</div>
96
- <h2 class="search-empty-title">No results found</h2>
97
- <p class="search-empty-text">Try a different search term or browse our categories.</p>
98
- </div>
99
- `
100
- return
101
- }
102
-
103
- resultsContainer.replaceChildren(
104
- el(
105
- 'div',
106
- { class: 'search-results-header' },
107
- el(
108
- 'span',
109
- { class: 'search-result-count' },
110
- `${results.length} result${results.length === 1 ? '' : 's'}`,
111
- ),
112
- ),
113
- el('div', { class: 'product-grid' }, ...results.map(renderProductCard)),
114
- )
115
- }
116
-
117
- client.search.subscribe(renderResults)
8
+ // The page owns the term and the category: the URL and the search input search, `nymbal-search-results` renders.
9
+ const searchInput = document.getElementById('search-input') as HTMLInputElement | null
118
10
 
119
- // Read initial query from URL
120
11
  const urlParams = new URLSearchParams(window.location.search)
121
12
  const initialQuery = urlParams.get('q')
122
13
 
123
14
  if (initialQuery) {
124
15
  if (searchInput) searchInput.value = initialQuery
125
- client.search.search(initialQuery)
126
- } else {
127
- renderResults()
16
+ client.search.search(initialQuery, { category: urlParams.get('category') })
128
17
  }
129
18
 
130
- // Listen for search input changes
19
+ // The category filter has narrowed the search; the URL follows, beside ?q.
20
+ document.addEventListener('nymbal:filter:changed', (event) => {
21
+ const { value } = (event as CustomEvent<{ name: string; value: string }>).detail
22
+ const url = new URL(window.location.href)
23
+ if (value) url.searchParams.set('category', value)
24
+ else url.searchParams.delete('category')
25
+ window.history.replaceState({}, '', url.toString())
26
+ })
27
+
28
+ // A new query searches every category again.
131
29
  searchInput?.addEventListener('input', () => {
132
30
  const value = searchInput.value.trim()
133
31
  if (value.length >= 2) {
134
32
  client.search.search(value)
135
- // Update URL without reload
136
33
  const url = new URL(window.location.href)
137
34
  url.searchParams.set('q', value)
35
+ url.searchParams.delete('category')
138
36
  window.history.replaceState({}, '', url.toString())
139
37
  } else if (value.length === 0) {
140
38
  client.search.clearSearch()
141
39
  const url = new URL(window.location.href)
142
40
  url.searchParams.delete('q')
41
+ url.searchParams.delete('category')
143
42
  window.history.replaceState({}, '', url.toString())
144
43
  }
145
44
  })
@@ -0,0 +1,4 @@
1
+ // Signed in or registered: on to the page that sent the shopper here (`?redirect`), or the account.
2
+ document.addEventListener('nymbal:auth:signed-in', () => {
3
+ window.location.href = new URLSearchParams(window.location.search).get('redirect') ?? '/account'
4
+ })
@@ -1,9 +1,9 @@
1
1
  /* ----------------------------------------------------------------
2
2
  Nymbal Global Styles
3
- Built on @nymbalhq/web-components design tokens (--nymbal-*)
3
+ Built on the design tokens in tokens.css (--nymbal-*)
4
4
  ---------------------------------------------------------------- */
5
5
 
6
- @import '@nymbalhq/web-components/tokens.css';
6
+ @import './tokens.css';
7
7
 
8
8
  /* ---- Reset ---- */
9
9
 
@@ -23,7 +23,15 @@ html {
23
23
  }
24
24
 
25
25
  body {
26
- font-family: var(--nymbal-font-family, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
26
+ font-family: var(
27
+ --nymbal-font-family,
28
+ system-ui,
29
+ -apple-system,
30
+ BlinkMacSystemFont,
31
+ 'Segoe UI',
32
+ Roboto,
33
+ sans-serif
34
+ );
27
35
  font-size: var(--nymbal-font-size-base, 1rem);
28
36
  font-weight: var(--nymbal-font-weight-normal, 400);
29
37
  line-height: 1.6;
@@ -68,7 +76,12 @@ a {
68
76
  text-decoration: none;
69
77
  }
70
78
 
71
- h1, h2, h3, h4, h5, h6 {
79
+ h1,
80
+ h2,
81
+ h3,
82
+ h4,
83
+ h5,
84
+ h6 {
72
85
  line-height: 1.2;
73
86
  font-weight: var(--nymbal-font-weight-bold, 700);
74
87
  overflow-wrap: break-word;
@@ -84,12 +97,24 @@ table {
84
97
 
85
98
  /* ---- Base typography ---- */
86
99
 
87
- h1 { font-size: 2rem; }
88
- h2 { font-size: 1.5rem; }
89
- h3 { font-size: 1.25rem; }
90
- h4 { font-size: 1.125rem; }
91
- h5 { font-size: 1rem; }
92
- h6 { font-size: var(--nymbal-font-size-sm, 0.875rem); }
100
+ h1 {
101
+ font-size: 2rem;
102
+ }
103
+ h2 {
104
+ font-size: 1.5rem;
105
+ }
106
+ h3 {
107
+ font-size: 1.25rem;
108
+ }
109
+ h4 {
110
+ font-size: 1.125rem;
111
+ }
112
+ h5 {
113
+ font-size: 1rem;
114
+ }
115
+ h6 {
116
+ font-size: var(--nymbal-font-size-sm, 0.875rem);
117
+ }
93
118
 
94
119
  small {
95
120
  font-size: var(--nymbal-font-size-sm, 0.875rem);
@@ -277,13 +302,13 @@ a:not([class]):focus-visible {
277
302
  /* ---- Form inputs ---- */
278
303
 
279
304
  .input,
280
- input[type="text"],
281
- input[type="email"],
282
- input[type="password"],
283
- input[type="search"],
284
- input[type="tel"],
285
- input[type="url"],
286
- input[type="number"],
305
+ input[type='text'],
306
+ input[type='email'],
307
+ input[type='password'],
308
+ input[type='search'],
309
+ input[type='tel'],
310
+ input[type='url'],
311
+ input[type='number'],
287
312
  select,
288
313
  textarea {
289
314
  display: block;
@@ -302,13 +327,13 @@ textarea {
302
327
  }
303
328
 
304
329
  .input:focus,
305
- input[type="text"]:focus,
306
- input[type="email"]:focus,
307
- input[type="password"]:focus,
308
- input[type="search"]:focus,
309
- input[type="tel"]:focus,
310
- input[type="url"]:focus,
311
- input[type="number"]:focus,
330
+ input[type='text']:focus,
331
+ input[type='email']:focus,
332
+ input[type='password']:focus,
333
+ input[type='search']:focus,
334
+ input[type='tel']:focus,
335
+ input[type='url']:focus,
336
+ input[type='number']:focus,
312
337
  select:focus,
313
338
  textarea:focus {
314
339
  outline: none;
@@ -339,24 +364,3 @@ label {
339
364
  color: var(--nymbal-color-error, #dc2626);
340
365
  margin-top: var(--nymbal-spacing-xs, 0.25rem);
341
366
  }
342
-
343
- /* ---- Product grid ---- */
344
-
345
- .product-grid {
346
- display: grid;
347
- gap: var(--nymbal-spacing-md, 1rem);
348
- grid-template-columns: 1fr;
349
- }
350
-
351
- @media (min-width: 641px) {
352
- .product-grid {
353
- grid-template-columns: repeat(2, 1fr);
354
- gap: var(--nymbal-spacing-lg, 1.5rem);
355
- }
356
- }
357
-
358
- @media (min-width: 1025px) {
359
- .product-grid {
360
- grid-template-columns: repeat(4, 1fr);
361
- }
362
- }
@@ -60,26 +60,6 @@
60
60
  max-width: 32rem;
61
61
  }
62
62
 
63
- .account-form-actions {
64
- display: flex;
65
- gap: var(--nymbal-spacing-sm, 0.5rem);
66
- margin-top: var(--nymbal-spacing-lg, 1.5rem);
67
- padding-top: var(--nymbal-spacing-lg, 1.5rem);
68
- border-top: 1px solid var(--nymbal-color-border, #e4e4e7);
69
- }
70
-
71
- .account-form-row {
72
- display: grid;
73
- grid-template-columns: 1fr;
74
- gap: var(--nymbal-spacing-md, 1rem);
75
- }
76
-
77
- @media (min-width: 641px) {
78
- .account-form-row {
79
- grid-template-columns: 1fr 1fr;
80
- }
81
- }
82
-
83
63
  /* ---- Order list ---- */
84
64
 
85
65
  .order-list {
@@ -189,70 +169,6 @@
189
169
  text-decoration: underline;
190
170
  }
191
171
 
192
- /* ---- Address cards ---- */
193
-
194
- .address-list {
195
- display: grid;
196
- grid-template-columns: 1fr;
197
- gap: var(--nymbal-spacing-md, 1rem);
198
- }
199
-
200
- @media (min-width: 641px) {
201
- .address-list {
202
- grid-template-columns: repeat(2, 1fr);
203
- }
204
- }
205
-
206
- .address-card {
207
- border: 1px solid var(--nymbal-color-border, #e4e4e7);
208
- border-radius: var(--nymbal-radius-lg, 0.75rem);
209
- padding: var(--nymbal-spacing-lg, 1.5rem);
210
- position: relative;
211
- }
212
-
213
- .address-card.default {
214
- border-color: var(--nymbal-color-accent, #2563eb);
215
- }
216
-
217
- .address-card-badge {
218
- display: inline-flex;
219
- font-size: var(--nymbal-font-size-xs, 0.75rem);
220
- font-weight: var(--nymbal-font-weight-medium, 500);
221
- color: var(--nymbal-color-accent, #2563eb);
222
- background-color: rgba(37, 99, 235, 0.08);
223
- padding: 2px var(--nymbal-spacing-sm, 0.5rem);
224
- border-radius: var(--nymbal-radius-full, 9999px);
225
- margin-bottom: var(--nymbal-spacing-sm, 0.5rem);
226
- }
227
-
228
- .address-card-name {
229
- font-size: var(--nymbal-font-size-sm, 0.875rem);
230
- font-weight: var(--nymbal-font-weight-bold, 700);
231
- color: var(--nymbal-color-primary, #18181b);
232
- margin-bottom: var(--nymbal-spacing-xs, 0.25rem);
233
- }
234
-
235
- .address-card-line {
236
- font-size: var(--nymbal-font-size-sm, 0.875rem);
237
- color: var(--nymbal-color-text-muted, #71717a);
238
- line-height: 1.5;
239
- }
240
-
241
- .address-card-actions {
242
- display: flex;
243
- gap: var(--nymbal-spacing-sm, 0.5rem);
244
- margin-top: var(--nymbal-spacing-md, 1rem);
245
- padding-top: var(--nymbal-spacing-md, 1rem);
246
- border-top: 1px solid var(--nymbal-color-border, #e4e4e7);
247
- }
248
-
249
- .address-card-actions button,
250
- .address-card-actions a {
251
- font-size: var(--nymbal-font-size-xs, 0.75rem);
252
- font-weight: var(--nymbal-font-weight-medium, 500);
253
- padding: var(--nymbal-spacing-xs, 0.25rem) var(--nymbal-spacing-sm, 0.5rem);
254
- }
255
-
256
172
  /* ---- Auth pages (login/register) ---- */
257
173
 
258
174
  .auth-page {
@@ -287,12 +203,6 @@
287
203
  margin-bottom: var(--nymbal-spacing-lg, 1.5rem);
288
204
  }
289
205
 
290
- .auth-card .btn {
291
- width: 100%;
292
- justify-content: center;
293
- margin-top: var(--nymbal-spacing-sm, 0.5rem);
294
- }
295
-
296
206
  .auth-footer {
297
207
  text-align: center;
298
208
  font-size: var(--nymbal-font-size-sm, 0.875rem);
@@ -44,6 +44,12 @@
44
44
 
45
45
  /* ---- Order details card ---- */
46
46
 
47
+ .confirmation-summary {
48
+ display: block;
49
+ margin-bottom: var(--nymbal-spacing-xl, 2rem);
50
+ text-align: left;
51
+ }
52
+
47
53
  .confirmation-card {
48
54
  text-align: left;
49
55
  background-color: #fafafa;
@@ -12,7 +12,12 @@
12
12
  text-align: center;
13
13
  padding: 4rem var(--nymbal-spacing-md, 1rem);
14
14
  min-height: 420px;
15
- background: linear-gradient(135deg, var(--nymbal-color-primary, #18181b) 0%, #27272a 50%, var(--nymbal-color-accent, #2563eb) 100%);
15
+ background: linear-gradient(
16
+ 135deg,
17
+ var(--nymbal-color-primary, #18181b) 0%,
18
+ #27272a 50%,
19
+ var(--nymbal-color-accent, #2563eb) 100%
20
+ );
16
21
  color: var(--nymbal-color-text-inverse, #fff);
17
22
  overflow: hidden;
18
23
  }
@@ -95,55 +100,6 @@
95
100
  border-color: rgba(255, 255, 255, 0.5);
96
101
  }
97
102
 
98
- /* ---- Category grid ---- */
99
-
100
- .category-grid {
101
- display: grid;
102
- grid-template-columns: repeat(2, 1fr);
103
- gap: var(--nymbal-spacing-md, 1rem);
104
- }
105
-
106
- @media (min-width: 641px) {
107
- .category-grid {
108
- grid-template-columns: repeat(4, 1fr);
109
- gap: var(--nymbal-spacing-lg, 1.5rem);
110
- }
111
- }
112
-
113
- .category-card {
114
- display: flex;
115
- flex-direction: column;
116
- align-items: center;
117
- justify-content: center;
118
- text-align: center;
119
- padding: var(--nymbal-spacing-lg, 1.5rem) var(--nymbal-spacing-md, 1rem);
120
- background-color: var(--nymbal-color-surface, #fff);
121
- border: 1px solid var(--nymbal-color-border, #e4e4e7);
122
- border-radius: var(--nymbal-radius-lg, 0.75rem);
123
- text-decoration: none;
124
- transition: border-color var(--nymbal-transition-fast, 150ms) ease,
125
- box-shadow var(--nymbal-transition-fast, 150ms) ease,
126
- transform var(--nymbal-transition-fast, 150ms) ease;
127
- min-height: 140px;
128
- }
129
-
130
- .category-card:hover {
131
- border-color: var(--nymbal-color-accent, #2563eb);
132
- box-shadow: var(--nymbal-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
133
- transform: translateY(-2px);
134
- }
135
-
136
- .category-card-icon {
137
- font-size: 2rem;
138
- margin-bottom: var(--nymbal-spacing-sm, 0.5rem);
139
- }
140
-
141
- .category-card-name {
142
- font-size: var(--nymbal-font-size-base, 1rem);
143
- font-weight: var(--nymbal-font-weight-medium, 500);
144
- color: var(--nymbal-color-text, #18181b);
145
- }
146
-
147
103
  /* ---- Featured products ---- */
148
104
 
149
105
  .featured-section {
@@ -170,53 +126,6 @@
170
126
  text-decoration: underline;
171
127
  }
172
128
 
173
- /* ---- Product card (shared home / PLP) ---- */
174
-
175
- .product-card {
176
- display: flex;
177
- flex-direction: column;
178
- background-color: var(--nymbal-color-surface, #fff);
179
- border: 1px solid var(--nymbal-color-border, #e4e4e7);
180
- border-radius: var(--nymbal-radius-lg, 0.75rem);
181
- overflow: hidden;
182
- text-decoration: none;
183
- transition: box-shadow var(--nymbal-transition-normal, 200ms) ease,
184
- transform var(--nymbal-transition-fast, 150ms) ease;
185
- }
186
-
187
- .product-card:hover {
188
- box-shadow: var(--nymbal-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
189
- transform: translateY(-2px);
190
- }
191
-
192
- .product-card-image {
193
- aspect-ratio: 1 / 1;
194
- width: 100%;
195
- object-fit: cover;
196
- background-color: #f4f4f5;
197
- }
198
-
199
- .product-card-body {
200
- padding: var(--nymbal-spacing-md, 1rem);
201
- flex: 1;
202
- display: flex;
203
- flex-direction: column;
204
- gap: var(--nymbal-spacing-xs, 0.25rem);
205
- }
206
-
207
- .product-card-name {
208
- font-size: var(--nymbal-font-size-sm, 0.875rem);
209
- font-weight: var(--nymbal-font-weight-medium, 500);
210
- color: var(--nymbal-color-text, #18181b);
211
- line-height: 1.3;
212
- }
213
-
214
- .product-card-price {
215
- font-size: var(--nymbal-font-size-sm, 0.875rem);
216
- font-weight: var(--nymbal-font-weight-bold, 700);
217
- color: var(--nymbal-color-primary, #18181b);
218
- }
219
-
220
129
  /* ---- Newsletter section ---- */
221
130
 
222
131
  .newsletter {
@@ -247,7 +156,7 @@
247
156
  justify-content: center;
248
157
  }
249
158
 
250
- .newsletter-form input[type="email"] {
159
+ .newsletter-form input[type='email'] {
251
160
  flex: 1;
252
161
  min-width: 200px;
253
162
  border-color: rgba(255, 255, 255, 0.2);
@@ -255,11 +164,11 @@
255
164
  color: var(--nymbal-color-text-inverse, #fff);
256
165
  }
257
166
 
258
- .newsletter-form input[type="email"]::placeholder {
167
+ .newsletter-form input[type='email']::placeholder {
259
168
  color: rgba(255, 255, 255, 0.5);
260
169
  }
261
170
 
262
- .newsletter-form input[type="email"]:focus {
171
+ .newsletter-form input[type='email']:focus {
263
172
  border-color: var(--nymbal-color-accent, #2563eb);
264
173
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
265
174
  }