astro-component-kit 0.4.0 → 0.4.1

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 (180) hide show
  1. package/README.md +105 -27
  2. package/package.json +16 -3
  3. package/src/components/index.ts +2 -0
  4. package/src/components/lib/Galaxy3D.astro +249 -137
  5. package/src/components/lib/app/AppSidebar.astro +249 -0
  6. package/src/components/lib/app/AppTopbar.astro +284 -0
  7. package/src/components/lib/app/ArrivalsBoard.astro +176 -0
  8. package/src/components/lib/app/AuthCard.astro +213 -0
  9. package/src/components/lib/app/DataTable.astro +346 -0
  10. package/src/components/lib/app/GuestProfileCard.astro +133 -0
  11. package/src/components/lib/app/HousekeepingBoard.astro +189 -0
  12. package/src/components/lib/app/PageHeader.astro +157 -0
  13. package/src/components/lib/app/PlanUsageCard.astro +112 -0
  14. package/src/components/lib/app/RoomRack.astro +207 -0
  15. package/src/components/lib/app/SettingsSection.astro +96 -0
  16. package/src/components/lib/app/SwitchRow.astro +71 -0
  17. package/src/components/lib/app/TeamMembers.astro +190 -0
  18. package/src/components/lib/app/app-icons.ts +52 -0
  19. package/src/components/lib/app/index.ts +19 -0
  20. package/src/components/lib/charts/AreaChart.astro +4 -4
  21. package/src/components/lib/charts/BarChart.astro +3 -3
  22. package/src/components/lib/charts/ChartFrame.astro +10 -10
  23. package/src/components/lib/charts/DonutChart.astro +2 -2
  24. package/src/components/lib/charts/DumbbellChart.astro +1 -1
  25. package/src/components/lib/charts/ForecastLineChart.astro +1 -1
  26. package/src/components/lib/charts/GaugeChart.astro +1 -1
  27. package/src/components/lib/charts/HeatmapChart.astro +1 -1
  28. package/src/components/lib/charts/HorizontalBarChart.astro +2 -2
  29. package/src/components/lib/charts/KpiStatTile.astro +4 -4
  30. package/src/components/lib/charts/LinearProgressGauge.astro +4 -4
  31. package/src/components/lib/charts/MetricDeltaTable.astro +3 -3
  32. package/src/components/lib/charts/RadarMetricChart.astro +4 -4
  33. package/src/components/lib/charts/RadialBarChart.astro +1 -1
  34. package/src/components/lib/charts/RetentionCohortTable.astro +2 -2
  35. package/src/components/lib/charts/TreemapChart.astro +1 -1
  36. package/src/components/lib/ecommerce/CartDrawer.astro +483 -0
  37. package/src/components/lib/ecommerce/CartPageList.astro +290 -0
  38. package/src/components/lib/ecommerce/CartToast.astro +276 -0
  39. package/src/components/lib/ecommerce/CategoryTiles.astro +143 -0
  40. package/src/components/lib/ecommerce/CheckoutForm.astro +423 -0
  41. package/src/components/lib/ecommerce/CountdownBanner.astro +230 -0
  42. package/src/components/lib/ecommerce/FilterBar.astro +278 -0
  43. package/src/components/lib/ecommerce/FilterSidebar.astro +405 -0
  44. package/src/components/lib/ecommerce/LiveActivityToast.astro +213 -0
  45. package/src/components/lib/ecommerce/OrderSummary.astro +295 -0
  46. package/src/components/lib/ecommerce/ProductBuyBox.astro +247 -0
  47. package/src/components/lib/ecommerce/ProductCardMinimal.astro +229 -0
  48. package/src/components/lib/ecommerce/ProductGallery.astro +145 -0
  49. package/src/components/lib/ecommerce/ProductGrid.astro +118 -0
  50. package/src/components/lib/ecommerce/ProductMedia.astro +113 -0
  51. package/src/components/lib/ecommerce/ProductRating.astro +78 -0
  52. package/src/components/lib/ecommerce/ProductToolbar.astro +93 -0
  53. package/src/components/lib/ecommerce/PromoBar.astro +145 -0
  54. package/src/components/lib/ecommerce/StockIndicator.astro +127 -0
  55. package/src/components/lib/ecommerce/StoreHeader.astro +446 -0
  56. package/src/components/lib/ecommerce/StoreHero.astro +260 -0
  57. package/src/components/lib/ecommerce/StoreProductCard.astro +307 -0
  58. package/src/components/lib/ecommerce/TrustBadges.astro +101 -0
  59. package/src/components/lib/ecommerce/VariantPicker.astro +145 -0
  60. package/src/components/lib/ecommerce/cart-store.ts +151 -0
  61. package/src/components/lib/ecommerce/catalog-filter.ts +62 -0
  62. package/src/components/lib/ecommerce/index.ts +34 -0
  63. package/src/components/lib/ecommerce/product-icons.ts +28 -0
  64. package/src/components/lib/ecommerce/types.ts +70 -0
  65. package/src/components/lib/forms/AnimatedInput.astro +4 -2
  66. package/src/components/lib/forms/CustomRadioGroup.astro +3 -1
  67. package/src/components/lib/index.ts +2 -0
  68. package/src/components/lib/layout/BentoGrid3.astro +6 -4
  69. package/src/components/lib/layout/CenterFixedBox.astro +2 -2
  70. package/src/components/lib/layout/CenteredContainer.astro +4 -2
  71. package/src/components/lib/layout/ClusterHorizontal.astro +2 -2
  72. package/src/components/lib/layout/FeatureHero.astro +4 -2
  73. package/src/components/lib/layout/FooterCenteredMinimal.astro +4 -2
  74. package/src/components/lib/layout/FullBleedSection.astro +2 -2
  75. package/src/components/lib/layout/GalleryGrid4.astro +2 -2
  76. package/src/components/lib/layout/HolyGrailGrid.astro +11 -9
  77. package/src/components/lib/layout/LandingSectionSplit.astro +5 -3
  78. package/src/components/lib/layout/MasonryGrid.astro +2 -2
  79. package/src/components/lib/layout/MinimalFooterGrid.astro +1 -1
  80. package/src/components/lib/layout/ScrollSnapSection.astro +1 -1
  81. package/src/components/lib/layout/SidebarLayoutWrapper.astro +4 -2
  82. package/src/components/lib/layout/SplitScreenLayout.astro +3 -3
  83. package/src/components/lib/layout/StepLayoutVertical.astro +1 -1
  84. package/src/components/lib/layout/StickySidebarWrapper.astro +4 -2
  85. package/src/components/lib/sections/AgencyContactBrief.astro +194 -0
  86. package/src/components/lib/sections/AgencyFooterCta.astro +94 -0
  87. package/src/components/lib/sections/AgencyHeader.astro +161 -0
  88. package/src/components/lib/sections/AgencyHeroKinetic.astro +172 -0
  89. package/src/components/lib/sections/AgencyServicesList.astro +113 -0
  90. package/src/components/lib/sections/AgencyTeamGrid.astro +111 -0
  91. package/src/components/lib/sections/AgencyWorkGrid.astro +147 -0
  92. package/src/components/lib/sections/BlogGridSection.astro +9 -7
  93. package/src/components/lib/sections/ButtonsShowcase.astro +20 -18
  94. package/src/components/lib/sections/CardsShowcase.astro +28 -26
  95. package/src/components/lib/sections/CareersOpenings.astro +10 -8
  96. package/src/components/lib/sections/ChangelogSection.astro +14 -7
  97. package/src/components/lib/sections/ContactGlassForm.astro +10 -8
  98. package/src/components/lib/sections/ContactSplitMap.astro +11 -9
  99. package/src/components/lib/sections/CtaAppDownload.astro +13 -11
  100. package/src/components/lib/sections/CtaGlowBanner.astro +12 -10
  101. package/src/components/lib/sections/CtaNewsletterSection.astro +13 -11
  102. package/src/components/lib/sections/CtaSplitImage.astro +13 -11
  103. package/src/components/lib/sections/DataShowcase.astro +23 -21
  104. package/src/components/lib/sections/FaqAccordionSection.astro +8 -8
  105. package/src/components/lib/sections/FaqTwoColumn.astro +9 -7
  106. package/src/components/lib/sections/FeatureAlternatingRows.astro +10 -8
  107. package/src/components/lib/sections/FeatureBentoGrid.astro +13 -11
  108. package/src/components/lib/sections/FeatureIconGrid.astro +6 -6
  109. package/src/components/lib/sections/FeatureTabsShowcase.astro +13 -11
  110. package/src/components/lib/sections/FeatureTimelineFlow.astro +8 -6
  111. package/src/components/lib/sections/FeedbackShowcase.astro +15 -13
  112. package/src/components/lib/sections/FooterMegaSection.astro +13 -11
  113. package/src/components/lib/sections/FormsShowcase.astro +22 -20
  114. package/src/components/lib/sections/HeroDashboardPreview.astro +11 -9
  115. package/src/components/lib/sections/HeroGlass.astro +22 -20
  116. package/src/components/lib/sections/HeroGradient.astro +15 -13
  117. package/src/components/lib/sections/HeroMinimalCentered.astro +10 -10
  118. package/src/components/lib/sections/HeroShowcaseSplit.astro +18 -16
  119. package/src/components/lib/sections/HeroSplit.astro +13 -11
  120. package/src/components/lib/sections/HeroTerminal.astro +14 -12
  121. package/src/components/lib/sections/HeroWaitlist.astro +10 -8
  122. package/src/components/lib/sections/HowItWorksSteps.astro +12 -10
  123. package/src/components/lib/sections/IntegrationsGrid.astro +7 -7
  124. package/src/components/lib/sections/LayoutShowcase.astro +21 -19
  125. package/src/components/lib/sections/LogoCloudSection.astro +4 -4
  126. package/src/components/lib/sections/LogoWallGrid.astro +6 -4
  127. package/src/components/lib/sections/NavigationShowcase.astro +15 -13
  128. package/src/components/lib/sections/OverlaysShowcase.astro +21 -19
  129. package/src/components/lib/sections/PricingComparisonMatrix.astro +15 -13
  130. package/src/components/lib/sections/PricingSinglePlan.astro +13 -11
  131. package/src/components/lib/sections/PricingTierCards.astro +11 -9
  132. package/src/components/lib/sections/PricingToggleCards.astro +13 -13
  133. package/src/components/lib/sections/ShopCategoryHero.astro +103 -0
  134. package/src/components/lib/sections/ShopCollectionMosaic.astro +131 -0
  135. package/src/components/lib/sections/ShopFooterWordmark.astro +100 -0
  136. package/src/components/lib/sections/ShopHeaderEditorial.astro +396 -0
  137. package/src/components/lib/sections/ShopHeroEditorial.astro +151 -0
  138. package/src/components/lib/sections/ShopLookbook.astro +250 -0
  139. package/src/components/lib/sections/ShopNewsletterEditorial.astro +128 -0
  140. package/src/components/lib/sections/ShopProductRail.astro +150 -0
  141. package/src/components/lib/sections/ShopReviewsEditorial.astro +133 -0
  142. package/src/components/lib/sections/ShopValuesMarquee.astro +91 -0
  143. package/src/components/lib/sections/StatsCounterBand.astro +9 -7
  144. package/src/components/lib/sections/StatsDashboardSection.astro +4 -2
  145. package/src/components/lib/sections/StatsMetricShowcase.astro +9 -7
  146. package/src/components/lib/sections/TeamGridSection.astro +12 -10
  147. package/src/components/lib/sections/TestimonialCarousel.astro +10 -8
  148. package/src/components/lib/sections/TestimonialFeatured.astro +7 -5
  149. package/src/components/lib/sections/TestimonialGridSection.astro +7 -7
  150. package/src/components/lib/sections/TestimonialWallGlass.astro +11 -9
  151. package/src/components/lib/sections/TypographyShowcase.astro +10 -8
  152. package/src/components/lib/sections/index.ts +10 -0
  153. package/src/components/lib/templates/ecommerce/StoreCategoryPage.astro +160 -0
  154. package/src/components/lib/templates/ecommerce/StoreCheckoutPage.astro +98 -0
  155. package/src/components/lib/templates/ecommerce/StoreHomePage.astro +191 -0
  156. package/src/components/lib/templates/ecommerce/StoreLayout.astro +169 -0
  157. package/src/components/lib/templates/ecommerce/StoreProductPage.astro +165 -0
  158. package/src/components/lib/templates/ecommerce/data.ts +187 -0
  159. package/src/components/lib/templates/ecommerce/index.ts +14 -0
  160. package/src/components/lib/templates/ecommerce-editorial/EditorialBagPage.astro +52 -0
  161. package/src/components/lib/templates/ecommerce-editorial/EditorialCheckoutPage.astro +123 -0
  162. package/src/components/lib/templates/ecommerce-editorial/EditorialHomePage.astro +102 -0
  163. package/src/components/lib/templates/ecommerce-editorial/EditorialLayout.astro +216 -0
  164. package/src/components/lib/templates/ecommerce-editorial/EditorialProductPage.astro +205 -0
  165. package/src/components/lib/templates/ecommerce-editorial/EditorialShopPage.astro +112 -0
  166. package/src/components/lib/templates/ecommerce-editorial/data.ts +125 -0
  167. package/src/components/lib/templates/ecommerce-editorial/index.ts +16 -0
  168. package/src/components/lib/templates/saas-hotel/AppLayout.astro +83 -0
  169. package/src/components/lib/templates/saas-hotel/DashboardPage.astro +66 -0
  170. package/src/components/lib/templates/saas-hotel/GuestsPage.astro +65 -0
  171. package/src/components/lib/templates/saas-hotel/LandingPage.astro +213 -0
  172. package/src/components/lib/templates/saas-hotel/ReportsPage.astro +63 -0
  173. package/src/components/lib/templates/saas-hotel/ReservationsPage.astro +49 -0
  174. package/src/components/lib/templates/saas-hotel/RoomsPage.astro +48 -0
  175. package/src/components/lib/templates/saas-hotel/SettingsPage.astro +115 -0
  176. package/src/components/lib/templates/saas-hotel/SignInPage.astro +50 -0
  177. package/src/components/lib/templates/saas-hotel/data.ts +229 -0
  178. package/src/components/lib/templates/saas-hotel/index.ts +19 -0
  179. package/src/components/lib/templates/saas-hotel/lobby-theme.css +90 -0
  180. package/src/styles/global.css +95 -0
@@ -0,0 +1,150 @@
1
+ ---
2
+ /**
3
+ * ShopProductRail — Horizontal, scroll-snapping product carousel of ProductCardMinimal cards with
4
+ * numbered items, prev/next buttons and a progress line.
5
+ *
6
+ * @param {string} eyebrow - Small label. Default 'Just in'.
7
+ * @param {string} title - Heading. Default 'New this *week*'.
8
+ * @param {Product[]} products - Products to show.
9
+ * @param {string} productBase - Product URL prefix (`${productBase}${slug}/`). Default '#'.
10
+ * @param {{label: string, href: string}} link - "View all" link (optional).
11
+ * @param {boolean} numbered - Show "N° 01" on cards. Default true.
12
+ * @param {string} currency - ISO currency. Default 'EUR'.
13
+ * @param {string} locale - Number format locale. Default 'en-GB'.
14
+ * @param {string} cartKey - Cart storage key. Default 'ack-cart'.
15
+ * @param {string} id - Rail id (unique per page). Default 'product-rail'.
16
+ * @param {string} size - Card width (sm|md|lg). Default 'md'.
17
+ * @param {string} className - Extra class.
18
+ *
19
+ * Accessibility: the rail is a labelled region (role="region") scrollable with keyboard;
20
+ * buttons have labels and disable at the ends; snapping is disabled with reduced motion.
21
+ */
22
+ import ProductCardMinimal from '../ecommerce/ProductCardMinimal.astro';
23
+ import type { Product } from '../ecommerce/types';
24
+
25
+ interface Props {
26
+ eyebrow?: string;
27
+ title?: string;
28
+ products?: Product[];
29
+ productBase?: string;
30
+ link?: { label: string; href: string };
31
+ numbered?: boolean;
32
+ currency?: string;
33
+ locale?: string;
34
+ cartKey?: string;
35
+ id?: string;
36
+ size?: 'sm' | 'md' | 'lg';
37
+ className?: string;
38
+ }
39
+
40
+ const {
41
+ eyebrow = 'Just in',
42
+ title = 'New this *week*',
43
+ products = [
44
+ { slug: 'linen-overshirt', name: 'Linen Overshirt', price: 145, category: 'clothing', badge: 'New', sizes: ['S', 'M', 'L'], colors: [{ name: 'Oat', hex: '#d9cbb3' }, { name: 'Ink', hex: '#22252b' }], icon: 'shirt', hue: 30 },
45
+ { slug: 'merino-crew', name: 'Merino Crew Knit', price: 160, category: 'clothing', sizes: ['S', 'M', 'L'], colors: [{ name: 'Sage', hex: '#8a9a7b' }], icon: 'jacket', hue: 95 },
46
+ { slug: 'field-tote', name: 'Field Tote', price: 120, category: 'bags', colors: [{ name: 'Rust', hex: '#9c3d1c' }], icon: 'bag', hue: 18 },
47
+ { slug: 'ash-vase', name: 'Ash Vase', price: 68, category: 'objects', icon: 'bottle', hue: 40 },
48
+ { slug: 'dune-loafer', name: 'Dune Loafer', price: 210, category: 'shoes', sizes: ['39', '40', '41', '42'], icon: 'sneaker', hue: 28 },
49
+ ],
50
+ productBase = '#',
51
+ link = { label: 'View all', href: '#' },
52
+ numbered = true,
53
+ currency = 'EUR',
54
+ locale = 'en-GB',
55
+ cartKey = 'ack-cart',
56
+ id = 'product-rail',
57
+ size = 'md',
58
+ className = '',
59
+ } = Astro.props;
60
+
61
+ const parts = title.split(/\*(.+?)\*/g);
62
+ const plainTitle = title.replace(/\*/g, '');
63
+ const hrefFor = (slug: string) => (productBase === '#' ? '#' : `${productBase}${slug}/`);
64
+ ---
65
+
66
+ <section class:list={['spr', `spr--${size}`, className]} data-rail>
67
+ <header class="spr__head">
68
+ <div>
69
+ {eyebrow && <p class="spr__eyebrow">{eyebrow}</p>}
70
+ <h2 class="spr__title">{parts.map((p, i) => (i % 2 ? <em>{p}</em> : p))}</h2>
71
+ </div>
72
+ <div class="spr__controls">
73
+ {link && <a class="spr__all" href={link.href}>{link.label}</a>}
74
+ <button type="button" class="spr__arrow" aria-label="Previous products" aria-controls={id} data-prev>←</button>
75
+ <button type="button" class="spr__arrow" aria-label="Next products" aria-controls={id} data-next>→</button>
76
+ </div>
77
+ </header>
78
+ <ul class="spr__track" id={id} role="region" aria-label={plainTitle} tabindex="0" data-track>
79
+ {products.map((p, i) => (
80
+ <li class="spr__slide">
81
+ <ProductCardMinimal product={p} href={hrefFor(p.slug)} index={numbered ? i + 1 : undefined} currency={currency} locale={locale} cartKey={cartKey} />
82
+ </li>
83
+ ))}
84
+ </ul>
85
+ <div class="spr__progress" aria-hidden="true"><span data-progress></span></div>
86
+ </section>
87
+
88
+ <script>
89
+ document.querySelectorAll<HTMLElement>('[data-rail]:not([data-bound])').forEach((rail) => {
90
+ rail.dataset.bound = '';
91
+ const track = rail.querySelector<HTMLElement>('[data-track]')!;
92
+ const prev = rail.querySelector<HTMLButtonElement>('[data-prev]')!;
93
+ const next = rail.querySelector<HTMLButtonElement>('[data-next]')!;
94
+ const bar = rail.querySelector<HTMLElement>('[data-progress]')!;
95
+ const step = () => (track.querySelector<HTMLElement>('.spr__slide')?.offsetWidth ?? 300) + 16;
96
+ const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
97
+ const update = () => {
98
+ const max = track.scrollWidth - track.clientWidth;
99
+ prev.disabled = track.scrollLeft <= 2;
100
+ next.disabled = track.scrollLeft >= max - 2;
101
+ const visible = track.clientWidth / track.scrollWidth;
102
+ bar.style.width = `${Math.max(10, visible * 100)}%`;
103
+ bar.style.transform = `translateX(${max > 0 ? (track.scrollLeft / max) * ((1 / Math.max(visible, 0.1)) - 1) * 100 : 0}%)`;
104
+ };
105
+ prev.addEventListener('click', () => track.scrollBy({ left: -step(), behavior: reduced ? 'auto' : 'smooth' }));
106
+ next.addEventListener('click', () => track.scrollBy({ left: step(), behavior: reduced ? 'auto' : 'smooth' }));
107
+ track.addEventListener('scroll', update, { passive: true });
108
+ window.addEventListener('resize', update);
109
+ update();
110
+ });
111
+ </script>
112
+
113
+ <style>
114
+ .spr { container-type: inline-size; --slide-w: clamp(220px, 24vw, 320px); color: var(--c-text-1, #fff); min-width: 0; }
115
+ .spr--sm { --slide-w: clamp(180px, 18vw, 240px); }
116
+ .spr--lg { --slide-w: clamp(260px, 30vw, 420px); }
117
+ .spr__head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-4, 1rem); margin-bottom: var(--sp-6, 1.5rem); }
118
+ .spr__eyebrow { margin: 0 0 var(--sp-2, 0.5rem); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--c-text-2, #a3a3a3); }
119
+ .spr__title { margin: 0; font-family: var(--font-display, Georgia, 'Times New Roman', serif); font-size: clamp(2rem, 4.5vw, 3.4rem); font-weight: 400; letter-spacing: -0.035em; line-height: 1; }
120
+ .spr__title em { font-style: italic; color: var(--shop-accent, var(--c-primary-light, #d4a017)); }
121
+ .spr__controls { display: flex; align-items: center; gap: var(--sp-2, 0.5rem); }
122
+ .spr__all { margin-right: var(--sp-3, 0.75rem); min-height: 44px; display: inline-flex; align-items: center; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: inherit; text-decoration: underline; text-underline-offset: 5px; }
123
+ .spr__arrow { width: 48px; height: 48px; border: 1px solid var(--c-border, rgba(255, 255, 255, 0.15)); border-radius: 50%; background: transparent; color: inherit; font-size: 1.1rem; cursor: pointer; transition: background 0.2s ease, color 0.2s ease, opacity 0.2s ease; }
124
+ .spr__arrow:hover:not(:disabled) { background: var(--c-text-1, #fff); color: var(--c-bg, #050505); }
125
+ .spr__arrow:disabled { opacity: 0.3; cursor: default; }
126
+ .spr :is(a, button):focus-visible, .spr__track:focus-visible { outline: 2px solid var(--shop-accent, var(--c-primary-light, #d4a017)); outline-offset: 3px; }
127
+
128
+ .spr__track {
129
+ display: grid;
130
+ grid-auto-flow: column;
131
+ grid-auto-columns: var(--slide-w);
132
+ gap: 16px;
133
+ margin: 0;
134
+ padding: 0 0 var(--sp-2, 0.5rem);
135
+ list-style: none;
136
+ overflow-x: auto;
137
+ scroll-snap-type: x mandatory;
138
+ scrollbar-width: none;
139
+ }
140
+ .spr__track::-webkit-scrollbar { display: none; }
141
+ .spr__slide { scroll-snap-align: start; min-width: 0; }
142
+ .spr__progress { position: relative; height: 1px; margin-top: var(--sp-5, 1.25rem); background: var(--c-border, rgba(255, 255, 255, 0.15)); overflow: hidden; }
143
+ .spr__progress span { position: absolute; inset: 0 auto 0 0; height: 100%; background: var(--c-text-1, #fff); transition: transform 0.15s linear; }
144
+
145
+ @container (max-width: 640px) {
146
+ .spr__track { grid-auto-columns: 72%; }
147
+ .spr__arrow, .spr__all { display: none; }
148
+ }
149
+ @media (prefers-reduced-motion: reduce) { .spr__track { scroll-snap-type: none; } }
150
+ </style>
@@ -0,0 +1,133 @@
1
+ ---
2
+ /**
3
+ * ShopReviewsEditorial — Customer voices as one large serif quote at a time, with product
4
+ * context, star rating, numbered pager and an overall score.
5
+ *
6
+ * @param {string} eyebrow - Small label. Default 'Customer notes'.
7
+ * @param {number} score - Average rating shown on the side. Default 4.9.
8
+ * @param {number} count - Total reviews. Default 1284.
9
+ * @param {Array<{quote: string, name: string, place?: string, product?: string, rating?: number}>} reviews - Quotes.
10
+ * @param {number} interval - Auto-advance in ms (0 = manual only). Default 7000.
11
+ * @param {string} variant - Style (plain|boxed). Default 'plain'.
12
+ * @param {string} className - Extra class.
13
+ *
14
+ * Accessibility: a labelled carousel region; slides are figures with figcaption; auto-advance
15
+ * pauses on hover/focus, is off with reduced motion, and pager buttons use aria-current.
16
+ */
17
+ import ProductRating from '../ecommerce/ProductRating.astro';
18
+
19
+ interface Review { quote: string; name: string; place?: string; product?: string; rating?: number }
20
+ interface Props {
21
+ eyebrow?: string;
22
+ score?: number;
23
+ count?: number;
24
+ reviews?: Review[];
25
+ interval?: number;
26
+ variant?: 'plain' | 'boxed';
27
+ className?: string;
28
+ }
29
+
30
+ const {
31
+ eyebrow = 'Customer notes',
32
+ score = 4.9,
33
+ count = 1284,
34
+ reviews = [
35
+ { quote: 'The overshirt has been washed forty times and it only gets softer. It is the first thing I reach for.', name: 'Inés M.', place: 'Valencia', product: 'Linen Overshirt', rating: 5 },
36
+ { quote: 'Packaging without plastic, a handwritten note, and a mug that feels made for my hands.', name: 'Jonas P.', place: 'Hamburg', product: 'Kiln Mug', rating: 5 },
37
+ { quote: 'They repaired the strap of my tote for free, two years after I bought it. That is rare.', name: 'Amira S.', place: 'Lyon', product: 'Field Tote', rating: 5 },
38
+ ],
39
+ interval = 7000,
40
+ variant = 'plain',
41
+ className = '',
42
+ } = Astro.props;
43
+ ---
44
+
45
+ <div class="sre-cq">
46
+ <section class:list={['sre', `sre--${variant}`, className]} aria-roledescription="carousel" aria-label={eyebrow} data-reviews data-interval={interval}>
47
+ <div class="sre__side">
48
+ <p class="sre__eyebrow">{eyebrow}</p>
49
+ <p class="sre__score">{score.toFixed(1)}<span>/5</span></p>
50
+ <ProductRating value={score} count={count} showValue={false} size="md" />
51
+ </div>
52
+ <div class="sre__main">
53
+ <div class="sre__slides">
54
+ {reviews.map((r, i) => (
55
+ <figure class:list={['sre__slide', { 'is-active': i === 0 }]} hidden={i !== 0} aria-roledescription="slide" aria-label={`${i + 1} of ${reviews.length}`} data-slide>
56
+ <blockquote class="sre__quote"><p>“{r.quote}”</p></blockquote>
57
+ <figcaption class="sre__by">
58
+ <strong>{r.name}</strong>{r.place && <span>, {r.place}</span>}
59
+ {r.product && <span class="sre__product">on {r.product}</span>}
60
+ </figcaption>
61
+ </figure>
62
+ ))}
63
+ </div>
64
+ {reviews.length > 1 && (
65
+ <div class="sre__pager" role="group" aria-label="Choose a review">
66
+ {reviews.map((_, i) => (
67
+ <button type="button" class="sre__page" aria-label={`Review ${i + 1}`} aria-current={i === 0 ? 'true' : undefined} data-page={i}>{String(i + 1).padStart(2, '0')}</button>
68
+ ))}
69
+ </div>
70
+ )}
71
+ </div>
72
+ </section>
73
+ </div>
74
+
75
+ <script>
76
+ document.querySelectorAll<HTMLElement>('[data-reviews]:not([data-bound])').forEach((root) => {
77
+ root.dataset.bound = '';
78
+ const slides = Array.from(root.querySelectorAll<HTMLElement>('[data-slide]'));
79
+ const pages = Array.from(root.querySelectorAll<HTMLButtonElement>('[data-page]'));
80
+ let index = 0;
81
+ let paused = false;
82
+ const go = (i: number) => {
83
+ index = (i + slides.length) % slides.length;
84
+ slides.forEach((s, n) => { s.hidden = n !== index; s.classList.toggle('is-active', n === index); });
85
+ pages.forEach((p, n) => { if (n === index) p.setAttribute('aria-current', 'true'); else p.removeAttribute('aria-current'); });
86
+ };
87
+ pages.forEach((p, i) => p.addEventListener('click', () => go(i)));
88
+ root.addEventListener('mouseenter', () => { paused = true; });
89
+ root.addEventListener('mouseleave', () => { paused = false; });
90
+ root.addEventListener('focusin', () => { paused = true; });
91
+ root.addEventListener('focusout', () => { paused = false; });
92
+ const ms = Number(root.dataset.interval) || 0;
93
+ if (ms > 0 && slides.length > 1 && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
94
+ window.setInterval(() => { if (!paused && !document.hidden) go(index + 1); }, ms);
95
+ }
96
+ });
97
+ </script>
98
+
99
+ <style>
100
+ .sre-cq { container-type: inline-size; }
101
+ .sre {
102
+ display: grid;
103
+ grid-template-columns: 240px minmax(0, 1fr);
104
+ gap: clamp(1.5rem, 5vw, 5rem);
105
+ padding-block: clamp(2rem, 5vw, 4rem);
106
+ border-top: 1px solid var(--c-text-1, #fff);
107
+ color: var(--c-text-1, #fff);
108
+ }
109
+ .sre--boxed { padding: clamp(1.5rem, 4vw, 3.5rem); border: 1px solid var(--c-border, rgba(255, 255, 255, 0.12)); background: var(--c-bg-1, #0a0a0a); }
110
+ .sre__eyebrow { margin: 0 0 var(--sp-4, 1rem); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--c-text-2, #a3a3a3); }
111
+ .sre__score { margin: 0 0 var(--sp-2, 0.5rem); font-family: var(--font-display, Georgia, 'Times New Roman', serif); font-size: 4.5rem; line-height: 1; letter-spacing: -0.04em; }
112
+ .sre__score span { font-size: 1.2rem; color: var(--c-text-2, #a3a3a3); }
113
+ .sre__side :global(.p-rating) { --p-rating-star: var(--shop-accent, var(--c-primary-light, #d4a017)); }
114
+
115
+ .sre__slides { display: grid; min-height: 12rem; }
116
+ .sre__slide { grid-area: 1 / 1; margin: 0; }
117
+ .sre__slide[hidden] { display: none; }
118
+ .sre__slide.is-active { animation: sre-in 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
119
+ @keyframes sre-in { from { opacity: 0; transform: translateY(10px); } }
120
+ .sre__quote { margin: 0; }
121
+ .sre__quote p { margin: 0; font-family: var(--font-display, Georgia, 'Times New Roman', serif); font-size: clamp(1.6rem, 3.4vw, 2.8rem); font-weight: 400; font-style: italic; line-height: 1.2; letter-spacing: -0.02em; text-wrap: balance; }
122
+ .sre__by { margin-top: var(--sp-5, 1.25rem); font-size: 0.9rem; }
123
+ .sre__by span { color: var(--c-text-2, #a3a3a3); }
124
+ .sre__product { display: block; margin-top: 2px; font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; }
125
+
126
+ .sre__pager { display: flex; gap: var(--sp-4, 1rem); margin-top: var(--sp-8, 2rem); }
127
+ .sre__page { min-width: 44px; min-height: 44px; padding: 0; border: 0; border-top: 1px solid var(--c-border, rgba(255, 255, 255, 0.2)); background: none; color: var(--c-text-2, #a3a3a3); font: inherit; font-size: 0.8rem; font-variant-numeric: tabular-nums; text-align: left; cursor: pointer; }
128
+ .sre__page[aria-current='true'] { border-top: 2px solid var(--shop-accent, var(--c-primary-light, #d4a017)); color: var(--c-text-1, #fff); }
129
+ .sre__page:focus-visible { outline: 2px solid var(--shop-accent, var(--c-primary-light, #d4a017)); outline-offset: 3px; }
130
+
131
+ @container (max-width: 760px) { .sre { grid-template-columns: 1fr; } }
132
+ @media (prefers-reduced-motion: reduce) { .sre__slide.is-active { animation: none; } }
133
+ </style>
@@ -0,0 +1,91 @@
1
+ ---
2
+ /**
3
+ * ShopValuesMarquee — Brand promise band: an oversized italic marquee followed by numbered
4
+ * value columns (materials, making, after-care…).
5
+ *
6
+ * @param {string[]} words - Marquee words. Empty hides the marquee.
7
+ * @param {Array<{title: string, text: string}>} values - 2–4 value columns.
8
+ * @param {number} speed - Marquee loop duration in seconds. Default 36.
9
+ * @param {string} variant - Style (plain|inverted). Default 'plain'.
10
+ * @param {string} size - Marquee scale (sm|md|lg). Default 'md'.
11
+ * @param {string} className - Extra class.
12
+ *
13
+ * Accessibility: the marquee is decorative (aria-hidden) and pauses on hover and with reduced
14
+ * motion; values are a real list with headings.
15
+ */
16
+ interface Props {
17
+ words?: string[];
18
+ values?: Array<{ title: string; text: string }>;
19
+ speed?: number;
20
+ variant?: 'plain' | 'inverted';
21
+ size?: 'sm' | 'md' | 'lg';
22
+ className?: string;
23
+ }
24
+
25
+ const {
26
+ words = ['Made slowly', 'Natural fibres', 'Small workshops', 'Repaired for life'],
27
+ values = [
28
+ { title: 'Honest materials', text: 'Linen, wool, organic cotton and stoneware — traceable to the mill and the kiln.' },
29
+ { title: 'Made in small runs', text: 'Each piece is produced in batches of a few hundred, never mass-manufactured.' },
30
+ { title: 'Kept for years', text: 'Free repairs on everything we make, and a buy-back for pieces you have outgrown.' },
31
+ ],
32
+ speed = 36,
33
+ variant = 'plain',
34
+ size = 'md',
35
+ className = '',
36
+ } = Astro.props;
37
+ ---
38
+
39
+ <section class:list={['svm', `svm--${variant}`, `svm--${size}`, className]} style={`--svm-speed: ${speed}s`}>
40
+ {words.length > 0 && (
41
+ <div class="svm__marquee" aria-hidden="true">
42
+ <div class="svm__track">
43
+ {[0, 1].map(() => words.map((w) => <span>{w}<i>✺</i></span>))}
44
+ </div>
45
+ </div>
46
+ )}
47
+ {values.length > 0 && (
48
+ <ol class="svm__values">
49
+ {values.map((v, i) => (
50
+ <li>
51
+ <span class="svm__num" aria-hidden="true">{String(i + 1).padStart(2, '0')}</span>
52
+ <h3>{v.title}</h3>
53
+ <p>{v.text}</p>
54
+ </li>
55
+ ))}
56
+ </ol>
57
+ )}
58
+ </section>
59
+
60
+ <style>
61
+ .svm { container-type: inline-size; --svm-fs: clamp(3rem, 9vw, 7.5rem); color: var(--c-text-1, #fff); overflow: hidden; }
62
+ .svm--sm { --svm-fs: clamp(2rem, 6vw, 4.5rem); }
63
+ .svm--lg { --svm-fs: clamp(4rem, 12vw, 10rem); }
64
+ .svm--inverted { background: var(--c-text-1, #fff); color: var(--c-bg, #050505); }
65
+ .svm__marquee { border-block: 1px solid currentColor; overflow: hidden; }
66
+ .svm__track { display: flex; width: max-content; animation: svm-run var(--svm-speed) linear infinite; }
67
+ .svm:hover .svm__track { animation-play-state: paused; }
68
+ .svm__track span { display: inline-flex; align-items: center; gap: 0.35em; padding: 0.1em 0 0.15em 0.35em; font-family: var(--font-display, Georgia, 'Times New Roman', serif); font-size: var(--svm-fs); font-style: italic; letter-spacing: -0.04em; line-height: 1.05; white-space: nowrap; }
69
+ .svm__track i { font-style: normal; font-size: 0.4em; color: var(--shop-accent, var(--c-primary-light, #d4a017)); }
70
+ @keyframes svm-run { to { transform: translateX(-50%); } }
71
+
72
+ .svm__values {
73
+ display: grid;
74
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
75
+ margin: 0 auto;
76
+ max-width: 1440px;
77
+ padding: clamp(2rem, 5vw, 4rem) clamp(1rem, 3vw, 2.5rem);
78
+ list-style: none;
79
+ gap: 0;
80
+ }
81
+ .svm__values li { padding: 0 var(--sp-6, 1.5rem) var(--sp-4, 1rem); border-left: 1px solid var(--c-border, rgba(255, 255, 255, 0.12)); }
82
+ .svm--inverted .svm__values li { border-left-color: rgb(var(--paper-rgb, 5 5 5) / 0.2); }
83
+ .svm__num { font-size: 0.75rem; font-variant-numeric: tabular-nums; color: var(--shop-accent, var(--c-primary-light, #d4a017)); }
84
+ .svm__values h3 { margin: var(--sp-3, 0.75rem) 0 var(--sp-2, 0.5rem); font-family: var(--font-display, Georgia, serif); font-size: 1.5rem; font-weight: 400; letter-spacing: -0.02em; }
85
+ .svm__values p { margin: 0; max-width: 22rem; font-size: 0.92rem; line-height: 1.65; opacity: 0.75; }
86
+
87
+ @container (max-width: 640px) {
88
+ .svm__values li { padding: var(--sp-4, 1rem) 0; border-left: 0; border-top: 1px solid var(--c-border, rgba(255, 255, 255, 0.12)); }
89
+ }
90
+ @media (prefers-reduced-motion: reduce) { .svm__track { animation: none; } }
91
+ </style>
@@ -39,6 +39,7 @@ const id = `scb-${Math.random().toString(36).slice(2, 8)}`;
39
39
  const fmt = (s: Stat) => `${s.prefix ?? ''}${s.value.toLocaleString('en-US', { minimumFractionDigits: s.decimals ?? 0, maximumFractionDigits: s.decimals ?? 0 })}${s.suffix ?? ''}`;
40
40
  ---
41
41
 
42
+ <div class="cq-stats-counter-band-cq">
42
43
  <section class={`scb scb--${variant} scb--${size} ${className}`} aria-labelledby={id}>
43
44
  <div class="scb__inner">
44
45
  <h2 id={id} class:list={['scb__title', { 'scb__sr': hideTitle }]}>{title}</h2>
@@ -52,7 +53,7 @@ const fmt = (s: Stat) => `${s.prefix ?? ''}${s.value.toLocaleString('en-US', { m
52
53
  </dl>
53
54
  </div>
54
55
  </section>
55
-
56
+ </div>
56
57
  <script>
57
58
  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
58
59
  const els = document.querySelectorAll<HTMLElement>('.scb__value[data-count]');
@@ -86,10 +87,11 @@ const fmt = (s: Stat) => `${s.prefix ?? ''}${s.value.toLocaleString('en-US', { m
86
87
  </script>
87
88
 
88
89
  <style>
90
+ .cq-stats-counter-band-cq { container-type: inline-size; }
89
91
  .scb {
90
92
  --scb-py: 4.5rem;
91
93
  padding: var(--scb-py) var(--sp-6, 1.5rem);
92
- background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.02) 50%, transparent), var(--c-bg, #050505);
94
+ background: linear-gradient(180deg, transparent, rgb(var(--ink-rgb, 255 255 255) / 0.02) 50%, transparent), var(--c-bg, #050505);
93
95
  }
94
96
  .scb--sm { --scb-py: 3rem; }
95
97
  .scb--lg { --scb-py: 6.5rem; }
@@ -98,9 +100,9 @@ const fmt = (s: Stat) => `${s.prefix ?? ''}${s.value.toLocaleString('en-US', { m
98
100
  .scb__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; margin: 0; }
99
101
  .scb__grid { margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
100
102
  .scb__item { display: flex; flex-direction: column-reverse; align-items: center; gap: 0.5rem; padding: 1.5rem 1rem; text-align: center; }
101
- .scb--divided .scb__item + .scb__item { border-left: 1px solid rgba(255, 255, 255, 0.08); }
103
+ .scb--divided .scb__item + .scb__item { border-left: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
102
104
  .scb--cards .scb__grid { gap: 1rem; }
103
- .scb--cards .scb__item { border-radius: var(--r-lg, 20px); background: rgba(255, 255, 255, 0.03); border: 1px solid var(--c-border, rgba(255, 255, 255, 0.07)); }
105
+ .scb--cards .scb__item { border-radius: var(--r-lg, 20px); background: rgb(var(--ink-rgb, 255 255 255) / 0.03); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07)); }
104
106
  .scb__value {
105
107
  margin: 0;
106
108
  font-size: clamp(2.25rem, 5vw, 3.25rem);
@@ -108,15 +110,15 @@ const fmt = (s: Stat) => `${s.prefix ?? ''}${s.value.toLocaleString('en-US', { m
108
110
  letter-spacing: -0.04em;
109
111
  line-height: 1;
110
112
  font-variant-numeric: tabular-nums;
111
- background: linear-gradient(180deg, #fff, rgba(255, 255, 255, 0.65));
113
+ background: linear-gradient(180deg, var(--c-text-1, #fff), rgb(var(--ink-rgb, 255 255 255) / 0.65));
112
114
  -webkit-background-clip: text;
113
115
  background-clip: text;
114
116
  color: transparent;
115
117
  }
116
118
  .scb__label { font-size: 0.9375rem; color: var(--c-text-2, #a3a3a3); max-width: 220px; }
117
- @media (max-width: 640px) {
119
+ @container (max-width: 640px) {
118
120
  .scb__grid { grid-template-columns: 1fr 1fr; }
119
121
  .scb--divided .scb__item + .scb__item { border-left: 0; }
120
- .scb--divided .scb__item:nth-child(even) { border-left: 1px solid rgba(255, 255, 255, 0.08); }
122
+ .scb--divided .scb__item:nth-child(even) { border-left: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
121
123
  }
122
124
  </style>
@@ -44,6 +44,7 @@ const {
44
44
  const id = `sds-${Math.random().toString(36).slice(2, 8)}`;
45
45
  ---
46
46
 
47
+ <div class="cq-stats-dashboard-section-cq">
47
48
  <section class={`sds sds--${size} ${className}`} aria-labelledby={id}>
48
49
  <div class="sds__inner">
49
50
  <header class="sds__head">
@@ -62,8 +63,9 @@ const id = `sds-${Math.random().toString(36).slice(2, 8)}`;
62
63
  </div>
63
64
  </div>
64
65
  </section>
65
-
66
+ </div>
66
67
  <style>
68
+ .cq-stats-dashboard-section-cq { container-type: inline-size; }
67
69
  .sds {
68
70
  --sds-py: 5.5rem;
69
71
  padding: var(--sds-py) var(--sp-6, 1.5rem);
@@ -79,5 +81,5 @@ const id = `sds-${Math.random().toString(36).slice(2, 8)}`;
79
81
  .sds__subtitle { margin: 0; font-size: 1.0625rem; color: var(--c-text-2, #a3a3a3); }
80
82
  .sds__kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.25rem; }
81
83
  .sds__charts { display: grid; grid-template-columns: 1.4fr 1fr; gap: 1.25rem; }
82
- @media (max-width: 900px) { .sds__charts { grid-template-columns: 1fr; } }
84
+ @container (max-width: 900px) { .sds__charts { grid-template-columns: 1fr; } }
83
85
  </style>
@@ -52,6 +52,7 @@ const stats = [
52
52
  ];
53
53
  ---
54
54
 
55
+ <div class="cq-stats-metric-showcase-cq">
55
56
  <section class={`stats-section ${className}`} aria-labelledby="stats-title">
56
57
  <div class="stats-bg" aria-hidden="true">
57
58
  <div class="stats-glow"></div>
@@ -105,8 +106,9 @@ const stats = [
105
106
  </div>
106
107
  </div>
107
108
  </section>
108
-
109
+ </div>
109
110
  <style>
111
+ .cq-stats-metric-showcase-cq { container-type: inline-size; }
110
112
  .stats-section {
111
113
  position: relative;
112
114
  padding: var(--sp-20, 5rem) var(--sp-6, 1.5rem);
@@ -185,7 +187,7 @@ const stats = [
185
187
  .stat-card {
186
188
  background: rgba(15, 23, 42, 0.6);
187
189
  backdrop-filter: blur(20px);
188
- border: 1px solid rgba(255, 255, 255, 0.08);
190
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
189
191
  border-radius: var(--r-xl, 20px);
190
192
  padding: 2rem 1.75rem;
191
193
  display: flex;
@@ -196,7 +198,7 @@ const stats = [
196
198
 
197
199
  .stat-card:hover {
198
200
  transform: translateY(-4px);
199
- border-color: rgba(255, 255, 255, 0.2);
201
+ border-color: rgb(var(--ink-rgb, 255 255 255) / 0.2);
200
202
  }
201
203
 
202
204
  .stat-top {
@@ -239,7 +241,7 @@ const stats = [
239
241
 
240
242
  .stat-chart-bar {
241
243
  height: 4px;
242
- background: rgba(255, 255, 255, 0.08);
244
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.08);
243
245
  border-radius: 9999px;
244
246
  overflow: hidden;
245
247
  }
@@ -258,7 +260,7 @@ const stats = [
258
260
  gap: 2rem;
259
261
  padding: 1.25rem 2rem;
260
262
  background: rgba(10, 15, 30, 0.8);
261
- border: 1px solid rgba(255, 255, 255, 0.08);
263
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
262
264
  border-radius: 16px;
263
265
  flex-wrap: wrap;
264
266
  }
@@ -313,13 +315,13 @@ const stats = [
313
315
  font-weight: 600;
314
316
  }
315
317
 
316
- @media (max-width: 1024px) {
318
+ @container (max-width: 1024px) {
317
319
  .stats-grid {
318
320
  grid-template-columns: repeat(2, 1fr);
319
321
  }
320
322
  }
321
323
 
322
- @media (max-width: 640px) {
324
+ @container (max-width: 640px) {
323
325
  .stats-grid {
324
326
  grid-template-columns: 1fr;
325
327
  }
@@ -60,6 +60,7 @@ const members = [
60
60
  ];
61
61
  ---
62
62
 
63
+ <div class="cq-team-grid-section-cq">
63
64
  <section class={`team-section ${className}`} aria-labelledby="team-title">
64
65
  <div class="team-bg" aria-hidden="true">
65
66
  <div class="team-glow"></div>
@@ -106,8 +107,9 @@ const members = [
106
107
  </div>
107
108
  </div>
108
109
  </section>
109
-
110
+ </div>
110
111
  <style>
112
+ .cq-team-grid-section-cq { container-type: inline-size; }
111
113
  .team-section {
112
114
  position: relative;
113
115
  padding: var(--sp-20, 5rem) var(--sp-6, 1.5rem);
@@ -186,7 +188,7 @@ const members = [
186
188
  .member-card {
187
189
  background: rgba(15, 23, 42, 0.55);
188
190
  backdrop-filter: blur(20px);
189
- border: 1px solid rgba(255, 255, 255, 0.08);
191
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
190
192
  border-radius: var(--r-xl, 20px);
191
193
  padding: 2rem 1.5rem;
192
194
  text-align: center;
@@ -198,7 +200,7 @@ const members = [
198
200
 
199
201
  .member-card:hover {
200
202
  transform: translateY(-4px);
201
- border-color: rgba(255, 255, 255, 0.2);
203
+ border-color: rgb(var(--ink-rgb, 255 255 255) / 0.2);
202
204
  }
203
205
 
204
206
  .member-avatar {
@@ -211,7 +213,7 @@ const members = [
211
213
  font-weight: 800;
212
214
  color: #fff;
213
215
  margin-bottom: 1.25rem;
214
- border: 2px solid rgba(255, 255, 255, 0.2);
216
+ border: 2px solid rgb(var(--ink-rgb, 255 255 255) / 0.2);
215
217
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
216
218
  }
217
219
 
@@ -246,8 +248,8 @@ const members = [
246
248
  width: 34px;
247
249
  height: 34px;
248
250
  border-radius: 8px;
249
- background: rgba(255, 255, 255, 0.05);
250
- border: 1px solid rgba(255, 255, 255, 0.08);
251
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.05);
252
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
251
253
  display: flex;
252
254
  align-items: center;
253
255
  justify-content: center;
@@ -258,8 +260,8 @@ const members = [
258
260
 
259
261
  .social-icon:hover {
260
262
  color: #fff;
261
- background: rgba(255, 255, 255, 0.12);
262
- border-color: rgba(255, 255, 255, 0.2);
263
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.12);
264
+ border-color: rgb(var(--ink-rgb, 255 255 255) / 0.2);
263
265
  }
264
266
 
265
267
  /* Hiring Banner */
@@ -314,13 +316,13 @@ const members = [
314
316
  box-shadow: 0 6px 20px rgba(168, 85, 247, 0.45);
315
317
  }
316
318
 
317
- @media (max-width: 1024px) {
319
+ @container (max-width: 1024px) {
318
320
  .team-grid {
319
321
  grid-template-columns: repeat(2, 1fr);
320
322
  }
321
323
  }
322
324
 
323
- @media (max-width: 640px) {
325
+ @container (max-width: 640px) {
324
326
  .team-grid {
325
327
  grid-template-columns: 1fr;
326
328
  }