This commit is contained in:
2026-10-05 13:52:45 -04:00
parent 9eb71e9855
commit e90ebde399
34 changed files with 465 additions and 581 deletions
+1
View File
@@ -0,0 +1 @@
{"branding":{"colorScheme":"light","fonts":[{"family":"Inter","role":"body"},{"family":"Roboto Condensed","role":"heading"}],"colors":{"primary":"#E8423B","secondary":"#40A9FF","accent":"#E8423B","background":"#FFFFFF","textPrimary":"#313130","link":"#3858E9"},"typography":{"fontFamilies":{"primary":"Inter","heading":"Roboto Condensed"},"fontStacks":{"heading":["Inter","sans-serif"],"body":["Times New Roman"],"paragraph":["Inter","sans-serif"]},"fontSizes":{"h1":"92.16px","h2":"92.16px","body":"20.736px"}},"spacing":{"baseUnit":4,"borderRadius":"2px"},"components":{"buttonPrimary":{"background":"#FFFFFF","textColor":"#E8423B","borderRadius":"0px","borderRadiusCorners":{"topLeft":"0px","topRight":"0px","bottomRight":"0px","bottomLeft":"0px"},"shadow":"none"}},"images":{"logo":"https://www.albinigroup.com/wp-content/themes/albinigroup/static/img/logo.svg","favicon":"https://www.albinigroup.com/wp-content/themes/albinigroup/static/img/favicons/favicon.ico","ogImage":null,"logoHref":"https://www.albinigroup.com/en/","logoAlt":"Albini Group Logo"},"personality":{"tone":"professional","energy":"medium","targetAudience":"business professionals"},"designSystem":{"framework":"custom","componentLibrary":""},"confidence":{"buttons":0.8,"colors":0.9,"overall":0.8500000000000001},"brandName":"Homepage","logo":"https://www.albinigroup.com/wp-content/themes/albinigroup/static/img/logo.svg"},"metadata":{"viewport":"width=device-width, initial-scale=1","theme-color":"#060606","generator":"WPML ver:4.6.11 stt:1,27;","language":"en-US","robots":"max-image-preview:large","title":"Homepage","msapplication-TileImage":"https://www.albinigroup.com/wp-content/uploads/2024/07/android-chrome-512x512-2-300x300.png","format-detection":"telephone=no","favicon":"https://www.albinigroup.com/wp-content/themes/albinigroup/static/img/favicons/favicon.ico","scrapeId":"01a10843-3309-76f1-87e6-86b2fe6dff94","sourceURL":"https://www.albinigroup.com/en/","url":"https://www.albinigroup.com/en/","statusCode":200,"contentType":"text/html; charset=UTF-8","timezone":"America/New_York","proxyUsed":"basic","indexId":"687378ad-1338-4a4e-890d-8f67d61cc28a","creditsUsed":1,"concurrencyLimited":false}}
+1
View File
@@ -0,0 +1 @@
{"branding":{"colorScheme":"light","fonts":[{"family":"Helvetica Neue","role":"body"},{"family":"Palatino Linotype","role":"heading"}],"colors":{"primary":"#2563EB","secondary":"#9CA3AF","accent":"#2F2727","background":"#2F2727","textPrimary":"#2F2727","link":"#6B7280"},"typography":{"fontFamilies":{"primary":"Helvetica Neue","heading":"Palatino Linotype"},"fontStacks":{"heading":["__swiss_888950","__swiss_Fallback_888950","Helvetica Neue","Helvetica","Arial","sans-serif"],"body":["__swiss_888950","__swiss_Fallback_888950","Helvetica Neue","Helvetica","Arial","sans-serif"],"paragraph":["__swiss_888950","__swiss_Fallback_888950","Helvetica Neue","Helvetica","Arial","sans-serif"]},"fontSizes":{"h1":"16px","h2":"48px","body":"16px"}},"spacing":{"baseUnit":4,"borderRadius":"0px"},"components":{"input":{"background":"#FFFFFF","textColor":"#2F2727","borderColor":"#D5D4D4","borderRadius":"9999px","borderRadiusCorners":{"topLeft":"9999px","topRight":"9999px","bottomRight":"9999px","bottomLeft":"9999px"},"shadow":"none"},"buttonPrimary":{"background":"#2F2727","textColor":"#FFFFFF","borderRadius":"9999px","borderRadiusCorners":{"topLeft":"9999px","topRight":"9999px","bottomRight":"9999px","bottomLeft":"9999px"},"shadow":"none"},"buttonSecondary":{"background":"#F5F5F5","textColor":"#2F2727","borderRadius":"9999px","borderRadiusCorners":{"topLeft":"9999px","topRight":"9999px","bottomRight":"9999px","bottomLeft":"9999px"},"shadow":"none"}},"images":{"logo":"https://content.camirafabrics.com/media/2gcngglc/camira-logo-light.svg?q=100&width=3840","favicon":"https://www.camirafabrics.com/favicon.ico","ogImage":null,"logoHref":"https://www.camirafabrics.com/us?meta=JTdCJTdE","logoAlt":"Camira Logo Light"},"personality":{"tone":"professional","energy":"medium","targetAudience":"business professionals"},"designSystem":{"framework":"tailwind","componentLibrary":""},"confidence":{"buttons":0.9,"colors":0.9,"overall":0.9},"brandName":"Camira","logo":"https://content.camirafabrics.com/media/2gcngglc/camira-logo-light.svg?q=100&width=3840"},"metadata":{"robots":"noindex","viewport":["width=device-width, initial-scale=1","width=device-width,initial-scale=1"],"title":"Camira | Leading Supplier of Contract Fabrics and Upholstery Solutions","google-site-verification":"9h1IFVr9Fye0MSBgBlu-rt6V1e3C34bL0IbfI6HsTLY","description":"Discover Camira's extensive range of contract fabrics, ideal for commercial and residential interiors. Explore our high-quality upholstery solutions tailored for offices, hotels, and more","language":"en-US","favicon":"https://www.camirafabrics.com/favicon.ico","scrapeId":"01a10842-a7a3-751a-8edc-a88a06d5d185","sourceURL":"https://www.camirafabrics.com/us","url":"https://www.camirafabrics.com/us","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","indexId":"1372bc68-234b-44eb-828a-ef652391b19a","creditsUsed":1,"concurrencyLimited":true,"concurrencyQueueDurationMs":13158},"warning":"This scrape job was throttled at your current concurrency limit. If you'd like to scrape faster, you can upgrade your plan."}
+1
View File
@@ -0,0 +1 @@
{"branding":{"colorScheme":"light","fonts":[{"family":"Kvadrat Neuzeit S","role":"body"},{"family":"Arial","role":"body"}],"colors":{"primary":"#000000","accent":"#000000","background":"#FFFFFF","textPrimary":"#231F20","link":"#000000"},"typography":{"fontFamilies":{"primary":"Kvadrat Neuzeit S","heading":"Kvadrat Neuzeit S"},"fontStacks":{"heading":["kvadrat_neuzeit_s","Arial","sans-serif"],"body":["kvadrat_neuzeit_s","Arial","sans-serif"],"paragraph":["kvadrat_neuzeit_s","Arial","sans-serif"]},"fontSizes":{"h1":"16px","h2":"25px","body":"16px"}},"spacing":{"baseUnit":8,"borderRadius":"0px"},"components":{"input":{"background":"#FFFFFF","textColor":"#231F20","borderColor":null,"borderRadius":"22.5px","borderRadiusCorners":{"topLeft":"22.5px","topRight":"22.5px","bottomRight":"22.5px","bottomLeft":"22.5px"},"shadow":"none"}},"images":{"logo":"https://www.kvadrat.dk/apple-touch-icon-180x180-precomposed.png","favicon":"https://www.kvadrat.dk/apple-touch-icon-precomposed.png","ogImage":null,"logoAlt":"Kvadrat"},"personality":{"tone":"professional","energy":"medium","targetAudience":"design professionals"},"designSystem":{"framework":"custom","componentLibrary":""},"confidence":{"buttons":0,"colors":0.9,"overall":0.45},"brandName":"Kvadrat","logo":"https://www.kvadrat.dk/apple-touch-icon-180x180-precomposed.png"},"metadata":{"title":"Kvadrat - The global design textile company","ogTitle":"Kvadrat - The global design textile company","HandheldFriendly":"true","apple-mobile-web-app-title":"Kvadrat","ogImage":"","og:type":"website","og:description":"Immerse yourself in a world of inspiring contemporary textiles with Kvadrat. Explore our elegant upholstery, captivating curtains, premium rugs, and high-quality and sustainable solutions and bring your interior projects to life.","viewport":["width=device-width, initial-scale=1.0","width=device-width,initial-scale=1,user-scalable=yes","width=device-width,initial-scale=1"],"language":"en","og:image:height":"630","googlebot":"noindex,indexifembedded","description":"Immerse yourself in a world of inspiring contemporary textiles with Kvadrat. Explore our elegant upholstery, captivating curtains, premium rugs, and high-quality and sustainable solutions and bring your interior projects to life.","mobile-web-app-capable":"yes","apple-mobile-web-app-capable":"yes","fragment":"!","apple-mobile-web-app-status-bar-style":"black","ogDescription":"Immerse yourself in a world of inspiring contemporary textiles with Kvadrat. Explore our elegant upholstery, captivating curtains, premium rugs, and high-quality and sustainable solutions and bring your interior projects to life.","robots":["index, follow","index, follow"],"og:title":"Kvadrat - The global design textile company","google-site-verification":"P2D50rlQuBWe5zCrhyaWdg25yf1EPkUj_T0QSD4Ux30","og:url":"/en","og:image:width":"1200","og:image":"","ogUrl":"/en","favicon":"https://www.kvadrat.dk/touch-icon-192x192.png","scrapeId":"01a10842-a78e-726d-8967-816fd3028c2a","sourceURL":"https://www.kvadrat.dk/en","url":"https://www.kvadrat.dk/en","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","indexId":"88c37cd9-deae-4af8-aa23-4a7ab4e5fba9","creditsUsed":1,"concurrencyLimited":false}}
+1
View File
@@ -0,0 +1 @@
{"branding":{"colorScheme":"light","fonts":[{"family":"MaharamNeue-Medium","role":"heading"},{"family":"MaharamNeue-Regular","role":"body"}],"colors":{"primary":"#1B7F9E","secondary":"#C18610","accent":"#07893E","background":"#EDEDE8","textPrimary":"#000000","link":"#07893E"},"typography":{"fontFamilies":{"primary":"MaharamNeue-Regular","heading":"MaharamNeue-Medium"},"fontStacks":{"heading":["MaharamNeue-Regular","Helvetica","Arial","sans-serif"],"body":["MaharamNeue-Regular","Helvetica","Arial","sans-serif"],"paragraph":["MaharamNeue-Medium","Helvetica","Arial","sans-serif"]},"fontSizes":{"h1":"14px","h2":"14px","body":"9px"}},"spacing":{"baseUnit":4,"borderRadius":"4px"},"components":{"input":{"background":"#FFFFFF","textColor":"#000000","borderColor":null,"borderRadius":"5px","borderRadiusCorners":{"topLeft":"5px","topRight":"5px","bottomRight":"5px","bottomLeft":"5px"},"shadow":"none"}},"images":{"logo":"https://www.maharam.com/apple-touch-icon.png","favicon":"https://www.maharam.com/assets/favicon-f4f91d83b92be42dcd21f5946f92e54c048fba408c22f621e58c1ac167f40f61.ico","ogImage":null,"logoAlt":"Maharam"},"personality":{"tone":"professional","energy":"medium","targetAudience":"design professionals"},"designSystem":{"framework":"custom","componentLibrary":""},"confidence":{"buttons":0,"colors":0.95,"overall":0.475},"brandName":"Maharam","logo":"https://www.maharam.com/apple-touch-icon.png"},"metadata":{"robots":"follow,index","language":"en","copyright":"Copyright 2026, Maharam","description":"Maharam is North America's leading creator of textiles for commercial and residential interiors.","keywords":"Maharam, Knoll Textiles, Edelman, Textiles, Fabrics, Wallcoverings, Privacy Curtains, Window Coverings, Drapery, Draperies, Digital Projects, Maharam Digital Projects, Panel, Wall Systems, Upholstery, Commercial, Contract, Residential, Interiors, Healthcare, Hospitality, Corporate, Collaboration, Twentieth Century, Agenda, Leather, Leather Tiles, Hides","revisit-after":"3 days","msapplication-config":"none","title":"Maharam","viewport":"width=device-width, initial-scale=1, shrink-to-fit=no","csrf-param":"authenticity_token","csrf-token":"RGnSJ-2JJQtSeKNBhGhi83R4MK-_INpAFYbIJ3oTKb8mZhGaapWN5MaGCQbFdeuvqDUnDre-FVlo80aieCFOWQ","favicon":"https://www.maharam.com/assets/favicon-f4f91d83b92be42dcd21f5946f92e54c048fba408c22f621e58c1ac167f40f61.ico","scrapeId":"01a10842-a79d-771c-ad1e-215713ca4db4","sourceURL":"https://www.maharam.com/","url":"https://www.maharam.com/","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","indexId":"f1f9c1aa-aeeb-4a2b-950e-cd5df6f4530f","creditsUsed":1,"concurrencyLimited":true,"concurrencyQueueDurationMs":6122},"warning":"This scrape job was throttled at your current concurrency limit. If you'd like to scrape faster, you can upgrade your plan."}
+1
View File
@@ -0,0 +1 @@
{"branding":{"colorScheme":"light","fonts":[{"family":"Noto Sans","role":"body"}],"colors":{"primary":"#00378C","secondary":"#0D6EFD","accent":"#00378C","background":"#FFFFFF","textPrimary":"#000000","link":"#003366"},"typography":{"fontFamilies":{"primary":"Noto Sans","heading":"Noto Sans"},"fontStacks":{"heading":["Noto Sans","sans-serif"],"body":["Noto Sans","sans-serif"],"paragraph":["Noto Sans","sans-serif"]},"fontSizes":{"h1":"48px","h2":"48px","body":"20px"}},"spacing":{"baseUnit":4,"borderRadius":"3px"},"components":{"buttonPrimary":{"background":"#00378C","textColor":"#FFFFFF","borderColor":"#00378C","borderRadius":"50px","borderRadiusCorners":{"topLeft":"50px","topRight":"50px","bottomRight":"50px","bottomLeft":"50px"},"shadow":"none"},"buttonSecondary":{"background":"#000000","textColor":"#FFFFFF","borderRadius":"0px","borderRadiusCorners":{"topLeft":"0px","topRight":"0px","bottomRight":"0px","bottomLeft":"0px"},"shadow":"none"}},"images":{"logo":"https://www.milliken.com/-/media/milliken/footer-v2/milliken-logo-footer.svg","favicon":"https://www.milliken.com/assets/images/favicons/milliken-favicon.ico","ogImage":"https://contenthub.milliken.com/api/public/content/142bbadfbf6c4309a5b627a101800bac?v=eaf7ac01&t=W1440xH720","logoHref":"https://www.milliken.com/en-us/","logoAlt":"Milliken & Company logo"},"personality":{"tone":"professional","energy":"medium","targetAudience":"business professionals and industries seeking high-performance textiles"},"designSystem":{"framework":"bootstrap","componentLibrary":""},"confidence":{"buttons":0.95,"colors":0.9,"overall":0.925},"brandName":"High","logo":"https://www.milliken.com/-/media/milliken/footer-v2/milliken-logo-footer.svg"},"metadata":{"description":"Discover Milliken’s high-performance textiles for apparel, automotive, military, and industrial markets engineered through material science and PFAS-free innovation.","ogTitle":"High-Performance Textiles & Fabric Innovation | Milliken","viewport":"width=device-width, initial-scale=1, shrink-to-fit=no","title":"High-Performance Textiles & Fabric Innovation | Milliken","facebook-domain-verification":"bb8bw417fkjl8mfe669ii98qp9i87s","og:title":"High-Performance Textiles & Fabric Innovation | Milliken","language":"en-US","og:description":"Discover","ogDescription":"Discover","keywords":"","ogUrl":"https://www.milliken.com/en-us/textiles","og:type":"website","og:image:height":"660","og:image:width":"1920","ogImage":"https://contenthub.milliken.com/api/public/content/142bbadfbf6c4309a5b627a101800bac?v=eaf7ac01&t=W1440xH720","og:image:alt":"Flame resist fabric testing with dummy being exposed to flames","og:url":"https://www.milliken.com/en-us/textiles","og:image":"https://contenthub.milliken.com/api/public/content/142bbadfbf6c4309a5b627a101800bac?v=eaf7ac01&t=W1440xH720","favicon":"https://www.milliken.com/assets/images/favicons/milliken-favicon.ico","scrapeId":"01a10843-32fe-73b8-b81f-534572f9f400","sourceURL":"https://textiles.milliken.com/","url":"https://www.milliken.com/en-us/textiles","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","indexId":"bd4adbc8-3a41-484e-a57a-d8832a065cce","creditsUsed":1,"concurrencyLimited":false}}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

+3 -162
View File
@@ -10,8 +10,7 @@
"dependencies": { "dependencies": {
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-router-dom": "^7.6.0", "react-router-dom": "^7.6.0"
"vgpu": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.52.0", "@playwright/test": "^1.52.0",
@@ -793,31 +792,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@modelcontextprotocol/core": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0.tgz",
"integrity": "sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==",
"license": "MIT",
"dependencies": {
"zod": "^4.2.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@modelcontextprotocol/server": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz",
"integrity": "sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/core": "2.0.0",
"zod": "^4.2.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@napi-rs/lzma-linux-x64-gnu": { "node_modules/@napi-rs/lzma-linux-x64-gnu": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
@@ -1302,58 +1276,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vgpu/adapter-mock": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@vgpu/adapter-mock/-/adapter-mock-0.5.0.tgz",
"integrity": "sha512-YtgXOFn1qI+8oTJenG2wWbQPA28V0CfE5UJs5Xqbt6qyR/xaZ5LHH2XWTwFcjnqn2b1xQCyTatJCMyyOdweUnw==",
"license": "MIT",
"dependencies": {
"@vgpu/core": "0.5.0"
}
},
"node_modules/@vgpu/adapter-node": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@vgpu/adapter-node/-/adapter-node-0.5.0.tgz",
"integrity": "sha512-lgzqFcLDejwRgwlaNStzkwLNU9b4aN2OoPpWqT1UyPmK521BSw2OIQKzAuDIkzNlREla6KNC12G6bthhOzZKzw==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@vgpu/core": "0.5.0",
"webgpu": "0.4.0"
}
},
"node_modules/@vgpu/core": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@vgpu/core/-/core-0.5.0.tgz",
"integrity": "sha512-z80DHRHViv6+FKKkPzggEtT1ZZs/WCKRQbzJuwYdA135DZaSbCMoRK4anhM+I/nOHuDpf7H3xjkwE6rfolWZUw==",
"license": "MIT",
"dependencies": {
"@vgpu/wgsl": "0.5.0"
}
},
"node_modules/@vgpu/wgsl": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@vgpu/wgsl/-/wgsl-0.5.0.tgz",
"integrity": "sha512-PDlDJem7J28C9bk+vPy2kYZEoW8+nYIx2oN4nu7/WF2V7ETx32BtmWcawwMbhiu/CVsobFafML0h0eqLRBNa3Q==",
"license": "MIT",
"dependencies": {
"@vgpu/wgsl-std": "0.5.0"
},
"peerDependencies": {
"@vgpu/adapter-node": ">=0.1.6 <1.0.0 || ^0.5.0-rc.0"
},
"peerDependenciesMeta": {
"@vgpu/adapter-node": {
"optional": true
}
}
},
"node_modules/@vgpu/wgsl-std": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@vgpu/wgsl-std/-/wgsl-std-0.5.0.tgz",
"integrity": "sha512-NjMyJxwy4evFNCSqFQy4EqAbZ7iU+NUyJYyG9tLUhbdDLzBmfZlk1S3PyRZ9olBFFfEiVW2JNVw10maPKLEs1A==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "4.7.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
@@ -1375,12 +1297,6 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
} }
}, },
"node_modules/@webgpu/types": {
"version": "0.1.72",
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.72.tgz",
"integrity": "sha512-0cF7RFM2edNoiIS1ODJp0/Gzv4/xSXhwoR0YCza+OWpJWtn4wmo9DvK91aLlH9+uUnwIriP7ZiC3WitmyhuzBw==",
"license": "BSD-3-Clause"
},
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.11.21", "version": "2.11.21",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz",
@@ -1473,6 +1389,7 @@
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@@ -1635,6 +1552,7 @@
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
@@ -1686,18 +1604,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pixelmatch": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.2.0.tgz",
"integrity": "sha512-xhcb4yHu9sM/G7foGzoLtXYcC0zHEaOXXjRKhGup0fw78Nf2Tkiapv4EQyMzrbcmQPsllAI7DbFY2UT7PlI9Pg==",
"license": "ISC",
"dependencies": {
"pngjs": "^7.0.0"
},
"bin": {
"pixelmatch": "bin/pixelmatch"
}
},
"node_modules/playwright": { "node_modules/playwright": {
"version": "1.63.0", "version": "1.63.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
@@ -1727,15 +1633,6 @@
"node": ">=20" "node": ">=20"
} }
}, },
"node_modules/pngjs": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
"integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
"license": "MIT",
"engines": {
"node": ">=14.19.0"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.28", "version": "8.5.28",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
@@ -1960,36 +1857,6 @@
"browserslist": ">= 4.21.0" "browserslist": ">= 4.21.0"
} }
}, },
"node_modules/vgpu": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/vgpu/-/vgpu-0.5.0.tgz",
"integrity": "sha512-isQfMHRTysYFfT8UWTzqQI/ROw2L7lVkj7xoUJlvIF6FjYxBNS/YNG7qp3tZ7ZvZZ3uwYjso+geQv/bEq5Hl3w==",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/server": "2.0.0",
"@vgpu/adapter-mock": "0.5.0",
"@vgpu/adapter-node": "0.5.0",
"@vgpu/core": "0.5.0",
"@vgpu/wgsl": "0.5.0",
"@vgpu/wgsl-std": "0.5.0",
"@webgpu/types": "^0.1.69",
"pixelmatch": "^7.2.0",
"pngjs": "^7.0.0",
"wgpu-matrix": "^3.4.2",
"zod": "^4.4.3"
},
"bin": {
"vgpu": "bin/vgpu.js"
},
"peerDependencies": {
"three": ">=0.180.0 <0.200.0"
},
"peerDependenciesMeta": {
"three": {
"optional": true
}
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "6.4.3", "version": "6.4.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
@@ -2065,38 +1932,12 @@
} }
} }
}, },
"node_modules/webgpu": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/webgpu/-/webgpu-0.4.0.tgz",
"integrity": "sha512-F5pimn3Aoi0zWjuRdiVs5TnrUwSzD2lESBohsIUsqyitWkGRQlXU2fhV6ycXlQTa1bvAf3sjqiUpBEpmSQ5ptA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@webgpu/types": "^0.1.69",
"debug": "^4.4.0"
}
},
"node_modules/wgpu-matrix": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/wgpu-matrix/-/wgpu-matrix-3.4.2.tgz",
"integrity": "sha512-IfZFbG7olEYBD76VCZy2TzqM57GKy3LT++AZh37tMHPPByO6R2h61zXGBZFNPi2IzxNivNi8y5dvze5PjaczjA==",
"license": "MIT"
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
},
"node_modules/zod": {
"version": "4.6.5",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
"integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
} }
} }
} }
+1 -2
View File
@@ -12,8 +12,7 @@
"dependencies": { "dependencies": {
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-router-dom": "^7.6.0", "react-router-dom": "^7.6.0"
"vgpu": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.52.0", "@playwright/test": "^1.52.0",
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
Copyright 2014 The DM Sans Project Authors (https://github.com/googlefonts/dm-fonts) Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic) NotoSans-Italic[wdth,wght].ttf: Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)
This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL http://scripts.sil.org/OFL
----------------------------------------------------------- -----------------------------------------------------------
Binary file not shown.
@@ -1,8 +1,8 @@
Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif) Copyright 2011 Google Inc. All Rights Reserved. RobotoCondensed-Italic[wght].ttf: Copyright 2011 Google Inc. All Rights Reserved.
This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL http://scripts.sil.org/OFL
----------------------------------------------------------- -----------------------------------------------------------
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

+14 -15
View File
@@ -1,21 +1,14 @@
@font-face { @font-face {
font-family: "Instrument Serif"; font-family: "Roboto Condensed";
src: url("/fonts/instrument-serif-regular.ttf") format("truetype"); src: url("/fonts/roboto-condensed.woff2") format("woff2");
font-weight: 400; font-weight: 100 900;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "Instrument Serif"; font-family: "Noto Sans";
src: url("/fonts/instrument-serif-italic.ttf") format("truetype"); src: url("/fonts/noto-sans.woff2") format("woff2");
font-weight: 400; font-weight: 100 900;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "DM Sans";
src: url("/fonts/dm-sans.woff2") format("woff2");
font-weight: 100 1000;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@@ -26,8 +19,10 @@
--line: #d9d9cf; --line: #d9d9cf;
--wash: #ecebe4; --wash: #ecebe4;
--accent: #8f725c; --accent: #8f725c;
--serif: "Instrument Serif", Georgia, serif; --industrial: #496678;
--sans: "DM Sans", Arial, sans-serif; --industrial-deep: #273f4d;
--serif: "Roboto Condensed", "Arial Narrow", sans-serif;
--sans: "Noto Sans", Arial, sans-serif;
--mono: "Courier New", monospace; --mono: "Courier New", monospace;
--page-gutter: clamp(20px, 4vw, 72px); --page-gutter: clamp(20px, 4vw, 72px);
color-scheme: light; color-scheme: light;
@@ -48,6 +43,10 @@ body {
margin: 0; margin: 0;
min-width: 320px; min-width: 320px;
} }
em {
font-style: normal;
font-weight: 600;
}
button, button,
input, input,
textarea, textarea,
+7 -5
View File
@@ -10,13 +10,15 @@ export default function Gallery({ preview = false }) {
<section className="gallery" id="developments" tabIndex={-1}> <section className="gallery" id="developments" tabIndex={-1}>
<SectionHeading <SectionHeading
centered centered
label="Selected developments" label={
preview ? "Featured fabric developments" : "Selected developments"
}
title={ title={
preview ? ( preview ? (
<> <>
Structure changes What we make.
<br /> <br />
<em>everything.</em> <em>What we can develop.</em>
</> </>
) : ( ) : (
<> <>
@@ -29,12 +31,12 @@ export default function Gallery({ preview = false }) {
> >
<p className="gallery__intro"> <p className="gallery__intro">
{preview {preview
? "From subtle surface structures to more expressive knitted textures, different yarn combinations and machine setups produce entirely different fabric characteristics." ? "A selection of knitted structures showing how yarn, composition and machine setup shape the final fabric character."
: "Selected developments are processed separately as reference samples to explore how a construction can translate after dyeing and finishing. RRS commercially manufactures greige fabric only."} : "Selected developments are processed separately as reference samples to explore how a construction can translate after dyeing and finishing. RRS commercially manufactures greige fabric only."}
</p> </p>
{preview && ( {preview && (
<Action to="/fabrics#developments" variant="outline"> <Action to="/fabrics#developments" variant="outline">
View developments Explore all fabrics
</Action> </Action>
)} )}
</SectionHeading> </SectionHeading>
+13 -30
View File
@@ -31,7 +31,9 @@
top: 10px; top: 10px;
} }
.site-header { .site-header {
border-top: 4px solid var(--industrial);
border-bottom: 1px solid var(--line); border-bottom: 1px solid var(--line);
background: #faf9f5;
} }
.site-header__inner { .site-header__inner {
display: flex; display: flex;
@@ -44,27 +46,11 @@
.brand { .brand {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 16px;
text-decoration: none;
color: var(--ink);
} }
.brand__mark { .brand__logo {
font-family: var(--serif); display: block;
font-size: 42px; width: 92px;
font-weight: 600; height: auto;
line-height: 1;
letter-spacing: -0.065em;
}
.brand__period {
color: var(--accent);
}
.brand__name {
border-left: 1px solid var(--line);
padding-left: 16px;
font-size: 9px;
line-height: 1.65;
text-transform: uppercase;
letter-spacing: 0.12em;
} }
.site-nav { .site-nav {
display: flex; display: flex;
@@ -100,15 +86,15 @@
gap: 30px; gap: 30px;
margin-left: 45px; margin-left: 45px;
padding: 12px 20px; padding: 12px 20px;
border: 1px solid var(--ink); border: 1px solid var(--industrial-deep);
border-radius: 30px; border-radius: 30px;
color: var(--ink); background: var(--industrial-deep);
color: var(--paper);
text-decoration: none; text-decoration: none;
font-size: 11px; font-size: 11px;
} }
.site-nav__contact:hover { .site-nav__contact:hover {
background: var(--ink); background: var(--industrial);
color: var(--paper);
} }
.site-header__toggle { .site-header__toggle {
display: none; display: none;
@@ -209,7 +195,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
min-height: 420px; min-height: 420px;
background: var(--ink); background: var(--industrial-deep);
color: var(--paper); color: var(--paper);
padding: 64px; padding: 64px;
border-radius: 6px; border-radius: 6px;
@@ -282,11 +268,8 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 0; gap: 0;
} }
.brand__mark { .brand__logo {
font-size: 35px; width: 78px;
}
.brand__name {
font-size: 8px;
} }
.site-header__toggle { .site-header__toggle {
display: flex; display: flex;
+9 -10
View File
@@ -7,14 +7,13 @@ import "./Layout.css";
function Brand() { function Brand() {
return ( return (
<Link className="brand" to="/" aria-label="RRS home"> <Link className="brand" to="/" aria-label="RRS home">
<span className="brand__mark"> <img
RRS<span className="brand__period">.</span> className="brand__logo"
</span> src="/images/RRSLOGO.png"
<span className="brand__name"> alt="Radhe Radhe Synthetics"
Radhe Radhe width="1024"
<br /> height="809"
Synthetics />
</span>
</Link> </Link>
); );
} }
@@ -68,7 +67,7 @@ function Header() {
to="/contact#whatsapp" to="/contact#whatsapp"
onClick={closeMenu} onClick={closeMenu}
> >
Let's talk <Arrow diagonal /> Request a quote <Arrow diagonal />
</Link> </Link>
</nav> </nav>
</div> </div>
@@ -93,7 +92,7 @@ function Footer() {
))} ))}
</nav> </nav>
<div className="site-footer__contact"> <div className="site-footer__contact">
<span className="site-footer__label">Start a conversation</span> <span className="site-footer__label">Commercial enquiries</span>
<Link className="site-footer__link" to="/contact#whatsapp"> <Link className="site-footer__link" to="/contact#whatsapp">
WhatsApp <Arrow diagonal /> WhatsApp <Arrow diagonal />
</Link> </Link>
+185 -62
View File
@@ -2,76 +2,124 @@
position: fixed; position: fixed;
inset: 0; inset: 0;
z-index: 1000; z-index: 1000;
display: grid;
grid-template-rows: auto 1fr;
overflow: hidden; overflow: hidden;
padding: clamp(24px, 3.5vw, 56px); color: var(--ink);
color: #f5f3ee; background: var(--paper);
background: #24231e;
transition:
opacity 650ms cubic-bezier(0.76, 0, 0.24, 1),
clip-path 650ms cubic-bezier(0.76, 0, 0.24, 1);
clip-path: inset(0);
} }
.loader--closing { .loader__scene {
opacity: 0;
clip-path: inset(0 0 100% 0);
pointer-events: none;
}
.loader__canvas,
.loader__veil {
position: absolute; position: absolute;
inset: 0; inset: 0;
width: 100%;
height: 100%;
} }
.loader__canvas { .loader__yarns {
display: block; position: absolute;
opacity: 0;
transition: opacity 180ms ease-out;
}
.loader__canvas[data-ready="true"] {
opacity: 1;
}
.loader__veil {
background: linear-gradient(90deg, #17171238, transparent 45%, #17171238);
}
.loader__topline,
.loader__center {
position: relative;
z-index: 1; z-index: 1;
top: 0;
left: 50%;
width: min(72vw, 920px);
height: 34%;
overflow: visible;
transform: translateX(-50%);
} }
.loader__topline { .loader__yarns path {
fill: none;
stroke: var(--industrial);
stroke-width: 5;
stroke-linecap: round;
vector-effect: non-scaling-stroke;
opacity: 0;
stroke-dasharray: 1200;
stroke-dashoffset: 1200;
animation: yarn-feed 780ms 480ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.loader__yarns path:nth-child(2),
.loader__yarns path:nth-child(6) {
stroke: var(--accent);
animation-delay: 580ms;
}
.loader__yarns path:nth-child(3),
.loader__yarns path:nth-child(5) {
animation-delay: 660ms;
}
.loader__yarns path:nth-child(4) {
stroke: var(--ink);
stroke-width: 6;
animation-delay: 540ms;
}
.loader__machine {
position: absolute;
z-index: 2;
inset: 5% 0 7%;
display: grid;
place-items: center;
clip-path: inset(0 0 100%);
animation: machine-reveal 900ms 680ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.loader__machine-image {
display: block;
width: auto;
height: min(84vh, 1050px);
max-width: 84vw;
object-fit: contain;
filter: grayscale(1) contrast(1.08);
transform: scale(1.04);
animation: machine-colour 850ms 1050ms ease-out forwards;
}
.loader__brand {
position: absolute;
z-index: 4;
left: 50%;
bottom: clamp(18px, 2.2vw, 36px);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
gap: 18px; gap: 18px;
font-size: 10px; padding: 10px 18px;
font-weight: 600; border: 1px solid #282a2324;
letter-spacing: 0.16em; border-radius: 999px;
text-transform: uppercase; background: #f5f3eee8;
backdrop-filter: blur(8px);
opacity: 0;
transform: translate(-50%, 14px);
animation: brand-arrive 450ms 1500ms ease-out forwards;
} }
.loader__center { .loader__logo {
align-self: center; width: 82px;
justify-self: center; height: auto;
text-align: center;
} }
.loader__eyebrow { .loader__message {
margin: 0 0 18px;
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.loader__title {
margin: 0; margin: 0;
padding-left: 18px;
border-left: 1px solid var(--line);
font-family: var(--serif); font-family: var(--serif);
font-size: clamp(58px, 8.5vw, 132px); font-size: 18px;
font-weight: 400; line-height: 1.1;
line-height: 0.84; white-space: nowrap;
letter-spacing: -0.055em;
} }
.loader__title em { .loader__handoff {
font-weight: 400; position: absolute;
z-index: 10;
inset: 0;
background:
repeating-linear-gradient(115deg, transparent 0 8px, #f5f3ee13 8px 9px),
var(--industrial-deep);
transform: translateX(-101%);
will-change: transform;
}
.loader--covering {
pointer-events: none;
}
.loader--covering .loader__handoff {
animation: handoff-cover 450ms cubic-bezier(0.76, 0, 0.24, 1) forwards;
}
.loader--revealing {
background: transparent;
pointer-events: none;
}
.loader--revealing .loader__scene {
opacity: 0;
}
.loader--revealing .loader__handoff {
transform: translateX(0);
animation: handoff-reveal 600ms cubic-bezier(0.76, 0, 0.24, 1) forwards;
} }
.loader__announcement { .loader__announcement {
position: absolute; position: absolute;
@@ -81,16 +129,91 @@
clip-path: inset(50%); clip-path: inset(50%);
white-space: nowrap; white-space: nowrap;
} }
@media (max-width: 600px) { @keyframes yarn-feed {
.loader__title { 0% {
font-size: clamp(54px, 19vw, 86px); opacity: 0;
stroke-dashoffset: 1200;
} }
.loader__topline span:last-child { 4% {
display: none; opacity: 0.82;
}
100% {
opacity: 0.82;
stroke-dashoffset: 0;
}
}
@keyframes machine-reveal {
to {
clip-path: inset(0);
}
}
@keyframes machine-colour {
to {
filter: grayscale(0) contrast(1);
transform: scale(1);
}
}
@keyframes brand-arrive {
to {
opacity: 1;
transform: translate(-50%, 0);
}
}
@keyframes handoff-cover {
to {
transform: translateX(0);
}
}
@keyframes handoff-reveal {
to {
transform: translateX(101%);
}
}
@media (max-width: 600px) {
.loader__yarns {
width: 108vw;
height: 32%;
}
.loader__machine {
inset: 9% 0 12%;
}
.loader__machine-image {
width: 98vw;
height: auto;
max-width: none;
}
.loader__brand {
bottom: 22px;
gap: 11px;
padding: 8px 12px;
}
.loader__logo {
width: 66px;
}
.loader__message {
padding-left: 11px;
font-size: 15px;
} }
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.loader { .loader__yarns,
transition: none; .loader__handoff {
display: none;
}
.loader__machine,
.loader__machine-image,
.loader__brand {
animation: none;
}
.loader__machine {
clip-path: inset(0);
}
.loader__machine-image {
filter: none;
transform: none;
}
.loader__brand {
opacity: 1;
transform: translate(-50%, 0);
} }
} }
+45 -79
View File
@@ -1,106 +1,72 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useState } from "react";
import weaveShader from "../shaders/loader.wgsl?raw";
import "./Loader.css"; import "./Loader.css";
export default function Loader() { export default function Loader() {
const canvasRef = useRef(null);
const [phase, setPhase] = useState("active"); const [phase, setPhase] = useState("active");
useEffect(() => { useEffect(() => {
const reducedMotion = window.matchMedia( const reducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)", "(prefers-reduced-motion: reduce)",
).matches; ).matches;
const closeTimer = window.setTimeout( const coveringTimer = window.setTimeout(
() => setPhase("closing"), () => setPhase("covering"),
reducedMotion ? 0 : 1900, reducedMotion ? 0 : 2100,
);
const revealTimer = window.setTimeout(
() => setPhase("revealing"),
reducedMotion ? 25 : 2550,
); );
const removeTimer = window.setTimeout( const removeTimer = window.setTimeout(
() => setPhase("hidden"), () => setPhase("hidden"),
reducedMotion ? 50 : 2550, reducedMotion ? 50 : 3150,
); );
return () => { return () => {
window.clearTimeout(closeTimer); window.clearTimeout(coveringTimer);
window.clearTimeout(revealTimer);
window.clearTimeout(removeTimer); window.clearTimeout(removeTimer);
}; };
}, []); }, []);
useEffect(() => {
if (phase !== "active") return undefined;
let active = true;
let gpu;
let loop;
let unsubscribeResize;
async function renderWeave() {
try {
const { clock, effect, frameLoop, init, surface } = await import(
"vgpu"
);
gpu = await init({ powerPreference: "low-power" });
if (!active) return gpu.dispose();
const target = surface(gpu, canvasRef.current, { dpr: [1, 1.5] });
const weave = effect(gpu, weaveShader, {
set: { params: { time: 0, progress: 0, aspect: 1, padding: 0 } },
});
const timer = clock(gpu);
unsubscribeResize = target.onResize(({ width, height }) => {
weave.set({ params: { aspect: width / height } });
});
let presented = false;
loop = frameLoop(
gpu,
(frame) => {
weave.set({
params: {
time: timer.time,
progress: Math.min(1.2, timer.time / 1.8),
},
});
frame.pass(target, weave);
if (!presented) {
presented = true;
requestAnimationFrame(() => {
if (active) canvasRef.current.dataset.ready = "true";
});
}
},
{ fps: 30 },
);
} catch {
// A flat background remains when WebGPU is unavailable.
}
}
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches)
renderWeave();
return () => {
active = false;
loop?.stop();
unsubscribeResize?.();
gpu?.dispose();
};
}, [phase]);
if (phase === "hidden") return null; if (phase === "hidden") return null;
return ( return (
<div className={`loader loader--${phase}`} aria-live="polite"> <div className={`loader loader--${phase}`} aria-live="polite">
<canvas className="loader__canvas" ref={canvasRef} aria-hidden="true" /> <div className="loader__scene">
<div className="loader__veil" aria-hidden="true" /> <svg
<div className="loader__topline"> className="loader__yarns"
<span>RRS / 1987</span> viewBox="0 0 1000 430"
<span>Surat, India</span> preserveAspectRatio="none"
aria-hidden="true"
>
<path pathLength="1" d="M60 0 C90 150 120 285 180 430" />
<path pathLength="1" d="M205 0 C230 155 255 310 285 430" />
<path pathLength="1" d="M350 0 C365 170 380 320 395 430" />
<path pathLength="1" d="M500 0 L500 430" />
<path pathLength="1" d="M650 0 C635 170 620 320 605 430" />
<path pathLength="1" d="M795 0 C770 155 745 310 715 430" />
<path pathLength="1" d="M940 0 C910 150 880 285 820 430" />
</svg>
<div className="loader__machine" aria-hidden="true">
<img
className="loader__machine-image"
src="/images/knitting-machine.webp"
alt=""
width="1113"
height="1316"
/>
</div> </div>
<div className="loader__center"> <div className="loader__brand">
<p className="loader__eyebrow">Preparing the loom</p> <img
<p className="loader__title"> className="loader__logo"
From yarn src="/images/RRSLOGO.png"
<br /> alt="Radhe Radhe Synthetics"
<em>to structure.</em> width="1024"
</p> height="809"
/>
<p className="loader__message">From yarn to repeat production.</p>
</div> </div>
</div>
<div className="loader__handoff" aria-hidden="true" />
<span className="loader__announcement"> <span className="loader__announcement">
Loading Radhe Radhe Synthetics Loading Radhe Radhe Synthetics
</span> </span>
+3 -3
View File
@@ -3,8 +3,8 @@
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 32px; gap: 32px;
border-block: 1px solid var(--line); border-block: 1px solid var(--line);
padding: 42px 0; padding: 38px 0 42px;
margin-top: 78px; margin-top: 42px;
} }
.stats__item { .stats__item {
min-width: 0; min-width: 0;
@@ -14,7 +14,7 @@
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
line-height: 1.6; line-height: 1.6;
color: var(--muted); color: var(--industrial);
} }
.stats__value { .stats__value {
font-family: var(--serif); font-family: var(--serif);
+15 -10
View File
@@ -12,25 +12,30 @@ export const company = Object.freeze({
export const navigation = [ export const navigation = [
{ to: "/", label: "Home" }, { to: "/", label: "Home" },
{ to: "/fabrics", label: "Fabrics" }, { to: "/fabrics", label: "Fabrics" },
{ to: "/about", label: "About" }, { to: "/about", label: "Manufacturing" },
{ to: "/contact", label: "Contact" }, { to: "/contact", label: "Contact" },
]; ];
export const capabilities = [ export const capabilities = [
{ {
title: "Develop", title: "01 · Develop",
subtitle: "Continuous fabric development", subtitle: "Define the construction",
text: "Different structures call for different yarns, blends and machine configurations. RRS continually develops new knitted constructions for evolving apparel requirements.", text: "The requirement begins with the intended application, yarn, blend, structure and fabric character.",
}, },
{ {
title: "Repeat", title: "02 · Configure",
subtitle: "Consistency in bulk production", subtitle: "Match yarn to machine",
text: "A successful development must remain dependable when production scales. RRS focuses on maintaining repeatability and quality across bulk manufacturing.", text: "Machine configuration and yarn selection are aligned to the construction being developed.",
}, },
{ {
title: "Manufacture", title: "03 · Manufacture",
subtitle: "Extensive circular-knitting capability", subtitle: "Circular knitting at scale",
text: "A broad range of circular-knitting configurations allows RRS to work across different structures, materials and fabric characteristics.", text: "The approved construction moves into greige production across the appropriate circular-knitting setup.",
},
{
title: "04 · Repeat",
subtitle: "Maintain bulk consistency",
text: "Successful developments are reproduced with repeatability across commercial manufacturing quantities.",
}, },
]; ];
+5 -5
View File
@@ -81,12 +81,12 @@ export default function About() {
return ( return (
<> <>
<PageHero <PageHero
label="About RRS" label="Manufacturing at RRS"
title={ title={
<> <>
Built around Built around
<br /> <br />
<em>the fabric.</em> <em>repeatable production.</em>
</> </>
} }
> >
@@ -100,12 +100,12 @@ export default function About() {
<section className="philosophy"> <section className="philosophy">
<SectionHeading <SectionHeading
centered centered
label="Our philosophy" label="Our manufacturing discipline"
title={ title={
<> <>
Consistency Develop with curiosity.
<br /> <br />
<em>and curiosity.</em> <em>Produce with consistency.</em>
</> </>
} }
> >
+5 -5
View File
@@ -52,19 +52,19 @@ export default function Contact() {
return ( return (
<> <>
<PageHero <PageHero
label="Contact RRS" label="Request a quote"
title={ title={
<> <>
Let’s talk Bring us your
<br /> <br />
<em>fabric.</em> <em>fabric requirement.</em>
</> </>
} }
texture="structured" texture="structured"
> >
<p> <p>
Whether you’re discussing an existing requirement, bulk production or Share an existing requirement, bulk quantity or new construction with
a new fabric construction, connect directly with RRS. RRS and begin a direct manufacturing conversation.
</p> </p>
</PageHero> </PageHero>
<section className="whatsapp" id="whatsapp" tabIndex={-1}> <section className="whatsapp" id="whatsapp" tabIndex={-1}>
+19 -12
View File
@@ -4,9 +4,8 @@
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
min-height: 590px; min-height: 768px;
margin-top: 24px; margin-inline: calc(0px - var(--page-gutter));
border-radius: 7px;
color: var(--paper); color: var(--paper);
background: var(--ink); background: var(--ink);
} }
@@ -21,11 +20,14 @@
.home-hero__shade { .home-hero__shade {
position: absolute; position: absolute;
inset: 0; inset: 0;
background: #1419108f; background:
linear-gradient(90deg, #132630e8 0%, #1f3239a8 54%, #17262f73 100%),
linear-gradient(0deg, #111b22b5 0%, transparent 45%);
} }
.home-hero__content { .home-hero__content {
position: relative; position: relative;
z-index: 1; z-index: 1;
max-width: 900px;
padding: 65px 62px 90px; padding: 65px 62px 90px;
} }
.home-hero__title { .home-hero__title {
@@ -51,8 +53,8 @@
.home-hero__bottom { .home-hero__bottom {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 32px; left: var(--page-gutter);
right: 32px; right: var(--page-gutter);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 20px; gap: 20px;
@@ -128,6 +130,9 @@
padding: 58px 0; padding: 58px 0;
border-bottom: 1px solid var(--line); border-bottom: 1px solid var(--line);
} }
.process__item:nth-child(even) {
background: linear-gradient(90deg, transparent, #49667808, transparent);
}
.process__item:nth-child(even) .process__visual { .process__item:nth-child(even) .process__visual {
order: -1; order: -1;
} }
@@ -137,9 +142,12 @@
} }
.process__verb { .process__verb {
margin: 0 0 24px; margin: 0 0 24px;
font-size: 14px; font-family: var(--mono);
font-size: 11px;
font-weight: 500; font-weight: 500;
color: var(--muted); color: var(--industrial);
letter-spacing: 0.05em;
text-transform: uppercase;
} }
.process__title { .process__title {
margin: 0 0 22px; margin: 0 0 22px;
@@ -175,19 +183,18 @@
} }
@media (max-width: 760px) { @media (max-width: 760px) {
.home-hero { .home-hero {
margin-top: 18px;
min-height: 560px; min-height: 560px;
} }
.home-hero__content { .home-hero__content {
padding: 42px 26px 130px; padding: 42px var(--page-gutter) 130px;
} }
.home-hero__title { .home-hero__title {
font-size: clamp(44px, 8.9vw, 68px); font-size: clamp(44px, 8.9vw, 68px);
margin-top: 32px; margin-top: 32px;
} }
.home-hero__bottom { .home-hero__bottom {
left: 26px; left: var(--page-gutter);
right: 26px; right: var(--page-gutter);
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
padding: 15px 0; padding: 15px 0;
+23 -31
View File
@@ -1,35 +1,22 @@
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { capabilities, company } from "../data"; import { capabilities } from "../data";
import { import {
Action, Action,
Arrow, Arrow,
Eyebrow, Eyebrow,
SectionHeading, SectionHeading,
Textile, Textile,
WhatsAppAction,
} from "../components/Primitives"; } from "../components/Primitives";
import { ContactBanner } from "../components/Layout"; import { ContactBanner } from "../components/Layout";
import Gallery from "../components/Gallery"; import Gallery from "../components/Gallery";
import Stats from "../components/Stats"; import Stats from "../components/Stats";
import "./Home.css"; import "./Home.css";
const processItems = [ const processItems = capabilities.map((capability, index) => ({
{ ...capability,
...capabilities[0], image: ["fine", "natural", "structured", "fine"][index],
image: "fine", href: index < 2 ? "/fabrics#applications" : "/fabrics#production",
href: "/fabrics#developments", }));
},
{
...capabilities[2],
image: "structured",
href: "/fabrics#circular-knitting",
},
{
...capabilities[1],
image: "natural",
href: "/fabrics#production",
},
];
export default function Home() { export default function Home() {
return ( return (
@@ -45,29 +32,35 @@ export default function Home() {
/> />
<div className="home-hero__shade" /> <div className="home-hero__shade" />
<div className="home-hero__content"> <div className="home-hero__content">
<Eyebrow>Circular knitted fabrics · RRS</Eyebrow> <Eyebrow>Specialist circular-knitting manufacturer · RRS</Eyebrow>
<h1 className="home-hero__title"> <h1 className="home-hero__title">
Built to develop. Built to develop.
<br /> <br />
<em>Built to repeat.</em> <em>Built to repeat.</em>
</h1> </h1>
<p className="home-hero__description">{company.description}</p> <p className="home-hero__description">
Greige knitted fabrics developed around yarn, structure and
application—then manufactured for consistent commercial supply.
</p>
<div className="home-hero__actions"> <div className="home-hero__actions">
<Action to="/fabrics" variant="light"> <Action to="/fabrics" variant="light">
Explore our fabrics Explore our fabrics
</Action> </Action>
<WhatsAppAction variant="ghost">Talk to RRS</WhatsAppAction> <Action to="/contact#whatsapp" variant="ghost">
Request a quote
</Action>
</div> </div>
</div> </div>
<div className="home-hero__bottom"> <div className="home-hero__bottom">
<span className="home-hero__tagline"> <span className="home-hero__tagline">
Consistency at scale. Continuous development. Development · Circular knitting · Bulk production
</span> </span>
<span className="home-hero__image-note"> <span className="home-hero__image-note">
Illustrative texture · Photography to follow Factory photography scheduled for content handover
</span> </span>
</div> </div>
</section> </section>
<Stats />
<Gallery preview /> <Gallery preview />
<section className="home-intro"> <section className="home-intro">
<div className="home-intro__heading"> <div className="home-intro__heading">
@@ -98,21 +91,20 @@ export default function Home() {
</Link> </Link>
</div> </div>
</section> </section>
<Stats />
<section className="process"> <section className="process">
<SectionHeading <SectionHeading
label="How we work" label="Manufacturing process"
title={ title={
<> <>
Behind the fabric: From requirement
<br /> <br />
<em>our process.</em> <em>to repeat production.</em>
</> </>
} }
> >
<p> <p>
Development, circular knitting and repeatable bulk production work A direct sequence connects fabric development with dependable
as one discipline. commercial manufacturing.
</p> </p>
</SectionHeading> </SectionHeading>
<div className="process__list"> <div className="process__list">
@@ -123,7 +115,7 @@ export default function Home() {
<h3 className="process__title">{subtitle}</h3> <h3 className="process__title">{subtitle}</h3>
<p className="process__text">{text}</p> <p className="process__text">{text}</p>
<Link className="process__link" to={href}> <Link className="process__link" to={href}>
Explore this capability <Arrow diagonal /> See our capability <Arrow diagonal />
</Link> </Link>
</div> </div>
<Textile className="process__visual" kind={image} /> <Textile className="process__visual" kind={image} />
-46
View File
@@ -1,46 +0,0 @@
struct Params {
time: f32,
progress: f32,
aspect: f32,
padding: f32,
}
@group(0) @binding(0) var<uniform> params: Params;
fn thread(value: f32, width: f32) -> f32 {
return pow(0.5 + 0.5 * cos(value), width);
}
@fragment
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
let p = vec2f(uv.x * params.aspect, uv.y);
let drift = sin(p.y * 18.0 + params.time * 0.42) * 0.018;
let warp = thread((p.x + drift) * 310.0, 7.0);
let weft = thread((p.y + sin(p.x * 13.0) * 0.006) * 280.0, 8.0);
let crossing = step(0.5, fract(floor(p.x * 49.0) * 0.5 + floor(p.y * 46.0) * 0.5));
let overUnder = mix(warp * 0.58 + weft, warp + weft * 0.55, crossing);
let irregularity = sin(p.x * 127.0 + sin(p.y * 31.0)) * 0.025;
let revealEdge = params.progress + sin(uv.y * 9.0 + params.time * 0.55) * 0.035;
let woven = 1.0 - smoothstep(revealEdge - 0.15, revealEdge + 0.04, uv.x);
let looseYarn = thread((p.x + sin(p.y * 8.0 + params.time) * 0.035) * 74.0, 10.0);
let wovenMask = smoothstep(0.16, 0.72, overUnder + irregularity);
let looseMask = smoothstep(0.24, 0.78, looseYarn);
let threadMask = mix(looseMask * 0.72, wovenMask, woven);
let slider = smoothstep(0.0, 1.0, params.progress);
let dyed = 1.0 - smoothstep(slider - 0.025, slider + 0.025, uv.x);
let greige = vec3f(0.69, 0.62, 0.51);
let madder = vec3f(0.72, 0.24, 0.12);
let indigo = vec3f(0.08, 0.34, 0.58);
let dyeColor = mix(madder, indigo, smoothstep(0.15, 0.9, uv.x));
let threadColor = mix(greige, dyeColor, dyed);
let shadow = vec3f(0.105, 0.10, 0.082);
let highlight = mix(vec3f(0.90, 0.84, 0.72), dyeColor * 1.35, dyed);
var color = mix(shadow, threadColor, threadMask);
color += highlight * pow(max(warp, weft), 10.0) * 0.14 * woven;
let vignette = smoothstep(0.82, 0.28, distance(uv, vec2f(0.5)));
color *= 0.7 + vignette * 0.3;
return vec4f(color, 1.0);
}
+13 -7
View File
@@ -5,7 +5,7 @@ const pages = [
["/", "Built to develop."], ["/", "Built to develop."],
["/fabrics", "Different yarns."], ["/fabrics", "Different yarns."],
["/about", "Built around"], ["/about", "Built around"],
["/contact", "Let’s talk"], ["/contact", "Bring us your"],
]; ];
for (const width of [1440, 768, 390, 320]) { for (const width of [1440, 768, 390, 320]) {
@@ -24,6 +24,7 @@ for (const width of [1440, 768, 390, 320]) {
heading, heading,
); );
await page.evaluate(() => document.fonts.ready); await page.evaluate(() => document.fonts.ready);
await expect(page.locator(".loader")).toHaveCount(0, { timeout: 3500 });
for (const image of await page.locator("img").all()) { for (const image of await page.locator("img").all()) {
await image.scrollIntoViewIfNeeded(); await image.scrollIntoViewIfNeeded();
await expect(image).toHaveJSProperty("complete", true); await expect(image).toHaveJSProperty("complete", true);
@@ -41,8 +42,8 @@ for (const width of [1440, 768, 390, 320]) {
(image) => !image.complete || !image.naturalWidth, (image) => !image.complete || !image.naturalWidth,
).length, ).length,
fontsLoaded: fontsLoaded:
document.fonts.check('16px "Instrument Serif"') && document.fonts.check('16px "Roboto Condensed"') &&
document.fonts.check('16px "DM Sans"'), document.fonts.check('16px "Noto Sans"'),
contentWidthRatio: contentWidthRatio:
document.querySelector("#main-content").getBoundingClientRect() document.querySelector("#main-content").getBoundingClientRect()
.width / window.innerWidth, .width / window.innerWidth,
@@ -109,7 +110,7 @@ test("mobile navigation closes on selection and Escape", async ({ page }) => {
await toggle.click(); await toggle.click();
await page await page
.getByRole("navigation", { name: "Main navigation" }) .getByRole("navigation", { name: "Main navigation" })
.getByRole("link", { name: "About", exact: true }) .getByRole("link", { name: "Manufacturing", exact: true })
.click(); .click();
await expect(page).toHaveURL("/about"); await expect(page).toHaveURL("/about");
await expect(toggle).toHaveAttribute("aria-expanded", "false"); await expect(toggle).toHaveAttribute("aria-expanded", "false");
@@ -191,7 +192,7 @@ test("every page section unveils once when scrolled into view", async ({
} }
}); });
test("homepage feature headings are centered semantic h2 elements", async ({ test("homepage presents proof, fabrics and process in that order", async ({
page, page,
}) => { }) => {
await page.goto("/"); await page.goto("/");
@@ -201,12 +202,17 @@ test("homepage feature headings are centered semantic h2 elements", async ({
}); });
const developmentsHeading = page.getByRole("heading", { const developmentsHeading = page.getByRole("heading", {
level: 2, level: 2,
name: "Structure changes everything.", name: "What we make. What we can develop.",
}); });
await expect(fabricsHeading).toHaveCSS("text-align", "center"); await expect(fabricsHeading).toHaveCSS("text-align", "center");
await expect(developmentsHeading.locator("..").locator("..")).toHaveClass( await expect(developmentsHeading.locator("..").locator("..")).toHaveClass(
/section-heading--centered/, /section-heading--centered/,
); );
const sections = await page
.locator("#main-content > section")
.evaluateAll((nodes) => nodes.map((node) => node.classList[0]));
expect(sections.indexOf("stats")).toBeLessThan(sections.indexOf("gallery"));
expect(sections.indexOf("gallery")).toBeLessThan(sections.indexOf("process"));
}); });
test("capacity values roll in opposite directions and settle at 60–70", async ({ test("capacity values roll in opposite directions and settle at 60–70", async ({
@@ -261,7 +267,7 @@ test("the woven loading page introduces the site and clears", async ({
await page.goto("/"); await page.goto("/");
const loader = page.locator(".loader"); const loader = page.locator(".loader");
await expect(loader).toContainText("Loading Radhe Radhe Synthetics"); await expect(loader).toContainText("Loading Radhe Radhe Synthetics");
await expect(page.locator(".loader__canvas")).toHaveCount(1); await expect(page.locator(".loader__machine-image")).toHaveCount(1);
await expect(loader).toHaveCount(0, { timeout: 3500 }); await expect(loader).toHaveCount(0, { timeout: 3500 });
}); });