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,93 +1,93 @@
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
----------------------------------------------------------- -----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
----------------------------------------------------------- -----------------------------------------------------------
PREAMBLE PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership open framework in which fonts may be shared and improved in partnership
with others. with others.
The OFL allows the licensed fonts to be used, studied, modified and The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives, names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives. to any document created using the fonts or their derivatives.
DEFINITIONS DEFINITIONS
"Font Software" refers to the set of files released by the Copyright "Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation. include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the "Reserved Font Name" refers to any names specified as such after the
copyright statement(s). copyright statement(s).
"Original Version" refers to the collection of Font Software components as "Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s). distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, "Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a Original Version, by changing formats or by porting the Font Software to a
new environment. new environment.
"Author" refers to any designer, engineer, programmer, technical "Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software. writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify, a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions: Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, 1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself. in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, 2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user. binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font 3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as Copyright Holder. This restriction only applies to the primary font name as
presented to the users. presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written Copyright Holder(s) and the Author(s) or with their explicit written
permission. permission.
5) The Font Software, modified or unmodified, in part or in whole, 5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created remain under this license does not apply to any document created
using the Font Software. using the Font Software.
TERMINATION TERMINATION
This license becomes null and void if any of the above conditions are This license becomes null and void if any of the above conditions are
not met. not met.
DISCLAIMER DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE. OTHER DEALINGS IN THE FONT SOFTWARE.
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
----------------------------------------------------------- -----------------------------------------------------------
@@ -18,7 +18,7 @@ with others.
The OFL allows the licensed fonts to be used, studied, modified and The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives, names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The however, cannot be released under any other type of license. The
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);
} }
} }
+46 -80
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"
</div> aria-hidden="true"
<div className="loader__center"> >
<p className="loader__eyebrow">Preparing the loom</p> <path pathLength="1" d="M60 0 C90 150 120 285 180 430" />
<p className="loader__title"> <path pathLength="1" d="M205 0 C230 155 255 310 285 430" />
From yarn <path pathLength="1" d="M350 0 C365 170 380 320 395 430" />
<br /> <path pathLength="1" d="M500 0 L500 430" />
<em>to structure.</em> <path pathLength="1" d="M650 0 C635 170 620 320 605 430" />
</p> <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 className="loader__brand">
<img
className="loader__logo"
src="/images/RRSLOGO.png"
alt="Radhe Radhe Synthetics"
width="1024"
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 });
}); });