mirror of
https://github.com/katanemo/plano.git
synced 2026-06-17 15:25:17 +02:00
feat(www): refactor landing page sections, add new diagrams and UI improvements
This commit is contained in:
parent
2a50b02d03
commit
94447c14e6
15 changed files with 996 additions and 63 deletions
440
www/package-lock.json
generated
440
www/package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
|||
"name": "website",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
@ -827,6 +828,12 @@
|
|||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
|
|
@ -842,6 +849,213 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
|
||||
"integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||
"@radix-ui/react-focus-guards": "1.1.3",
|
||||
"@radix-ui/react-focus-scope": "1.1.7",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-portal": "1.1.9",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"aria-hidden": "^1.2.4",
|
||||
"react-remove-scroll": "^2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
||||
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-scope": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
|
|
@ -860,6 +1074,91 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-effect-event": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
||||
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||
|
|
@ -1164,12 +1463,24 @@
|
|||
"version": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001751",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz",
|
||||
|
|
@ -1234,6 +1545,12 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
|
|
@ -1275,6 +1592,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/get-nonce": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
|
|
@ -1741,6 +2067,75 @@
|
|||
"react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
|
||||
"integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.7",
|
||||
"react-style-singleton": "^2.2.3",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.3",
|
||||
"use-sidecar": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll-bar": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-style-singleton": "^2.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-nonce": "^1.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||
|
|
@ -1902,6 +2297,49 @@
|
|||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/use-callback-ref": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"format": "biome format --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
|
|||
127
www/public/HowItWorks.svg
Normal file
127
www/public/HowItWorks.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 215 KiB |
97
www/public/NetworkPathDiagram.svg
Normal file
97
www/public/NetworkPathDiagram.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 70 KiB |
BIN
www/public/fonts/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf
Normal file
BIN
www/public/fonts/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf
Normal file
Binary file not shown.
|
|
@ -12,6 +12,14 @@
|
|||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IBM Plex Sans";
|
||||
src: url("/fonts/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf") format("truetype-variations");
|
||||
font-weight: 100 700; /* Variable font weight range */
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { IntroSection } from "@/components/IntroSection";
|
|||
import { IdeaToAgentSection } from "@/components/IdeaToAgentSection";
|
||||
import { UseCasesSection } from "@/components/UseCasesSection";
|
||||
import { VerticalCarouselSection } from "@/components/VerticalCarouselSection";
|
||||
import { HowItWorksSection } from "@/components/HowItWorksSection";
|
||||
import { UnlockPotentialSection } from "@/components/UnlockPotentialSection";
|
||||
import { Footer } from "@/components/Footer";
|
||||
import { LogoCloud } from "@/components/LogoCloud";
|
||||
|
|
@ -22,6 +23,7 @@ export default function Home() {
|
|||
<IdeaToAgentSection />
|
||||
<UseCasesSection />
|
||||
<VerticalCarouselSection />
|
||||
<HowItWorksSection />
|
||||
<UnlockPotentialSection variant="black" />
|
||||
|
||||
{/* Rest of the sections will be refactored next */}
|
||||
|
|
|
|||
|
|
@ -14,23 +14,23 @@ export function Hero() {
|
|||
{/* Version Badge */}
|
||||
<div className="mb-6">
|
||||
<div className="inline-flex items-center gap-2 px-4 py-1 rounded-full bg-[rgba(185,191,255,0.4)] border border-[var(--secondary)] shadow backdrop-blur">
|
||||
<span className="text-sm font-medium text-black/65">v0.3.12</span>
|
||||
<span className="text-sm font-medium text-black/65">v0.4</span>
|
||||
<span className="text-sm font-medium text-black">—</span>
|
||||
<span className="text-sm font-[600] tracking-[-0.6px]! text-black">RAG Agent Launch!</span>
|
||||
<span className="text-sm font-[600] tracking-[-0.6px]! text-black">Unified /v1/responses API with state management</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Heading */}
|
||||
<h1 className="text-5xl lg:text-7xl font-normal leading-tight tracking-tight text-black mb-4 flex flex-col -space-y-3">
|
||||
<span className="font-sans">The AI-native </span>
|
||||
<span className="font-sans font-medium text-[var(--secondary)]">network for agents</span>
|
||||
<span className="font-sans">Models-native </span>
|
||||
<span className="font-sans font-medium text-[var(--secondary)]">dataplane for agents</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Subheading with CTA Buttons on the right */}
|
||||
<div className="max-w-7xl flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6">
|
||||
<p className="text-xl lg:text-2xl font-sans font-[500] tracking-[-1.92px]! text-black max-w-2xl">
|
||||
Build and scale AI agents without handling the low-level plumbing.
|
||||
<p className="text-xl lg:text-2xl font-sans font-[500] tracking-[-1.92px]! text-black max-w-4xl">
|
||||
Build agents faster, and scale them reliably by offloading the plumbing work in AI agents.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
|
|
|
|||
39
www/src/components/HowItWorksSection.tsx
Normal file
39
www/src/components/HowItWorksSection.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
export function HowItWorksSection() {
|
||||
return (
|
||||
<section className="bg-[#1a1a1a] text-white py-24 px-6 lg:px-[102px]">
|
||||
<div className="max-w-324 mx-auto">
|
||||
<div className="flex flex-col gap-16">
|
||||
{/* Header and Description */}
|
||||
<div className="max-w-4xl">
|
||||
<h2 className="font-sans font-normal text-3xl lg:text-4xl tracking-[-2.88px]! text-white leading-[1.03] mb-8">
|
||||
How it works
|
||||
</h2>
|
||||
<div className="font-mono text-white text-xl lg:text-lg leading-10 tracking-[-1.2px]!">
|
||||
<p className="mb-0">
|
||||
Plano offers a delightful developer experience with a simple configuration file that describes the types of prompts your agentic app supports, a set of APIs that need to be plugged in for agentic scenarios (including retrieval queries) and your choice of LLMs.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Large Diagram */}
|
||||
<div className="w-full">
|
||||
<Image
|
||||
src="/HowItWorks.svg"
|
||||
alt="How Plano Works Diagram"
|
||||
width={1200}
|
||||
height={600}
|
||||
className="w-full h-auto"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -9,35 +9,35 @@ const carouselData = [
|
|||
id: 1,
|
||||
category: "LAUNCH FASTER",
|
||||
title: "Focus on core objectives",
|
||||
description: "Plano handles the heavy lifting for agentic apps with purpose-built LLMs that automate request clarification, query routing, and data extraction. Ship faster without juggling prompt engineering or infrastructure details.",
|
||||
description: "Building AI agents is hard enough (iterate on prompts and evaluate LLMs, etc), the plumbing work shouldn't add to that complexity. Plano takes care of the critical plumbing work like routing and orchestration to agents that slows you down and locks you into rigid frameworks, freeing developers to innovate on what truly matters.",
|
||||
image: "/LaunchFaster.svg"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category: "SCALE EFFICIENTLY",
|
||||
title: "Build without limits",
|
||||
description: "From prototype to production, Plano scales with your needs. Handle thousands of concurrent requests while maintaining consistent performance and reliability across your agent network.",
|
||||
image: "/ShipConfidently.svg"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category: "DEPLOY CONFIDENTLY",
|
||||
title: "Production-ready infrastructure",
|
||||
description: "Enterprise-grade security, monitoring, and governance built-in. Deploy your agents with confidence knowing they're protected by battle-tested infrastructure and compliance frameworks.",
|
||||
category: "BUILD WITH CHOICE",
|
||||
title: "Rapidly incorporate LLMs",
|
||||
description: "Build with multiple LLMs or model versions with a single unified API. Plano centralizes access controls, offers resiliency for traffic to 100+ LLMs -- all without you having to write a single line of code.",
|
||||
image: "/BuildWithChoice.svg"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: "INTEGRATE SEAMLESSLY",
|
||||
title: "Connect everything",
|
||||
description: "Unified API access across all major LLM providers. Switch between models, combine capabilities, and route requests intelligently without vendor lock-in or complex integrations.",
|
||||
id: 3,
|
||||
category: "RICH LEARNING SIGNALS",
|
||||
title: "Hyper-rich agent traces and logs",
|
||||
description: "Knowing when agents fail or delight users is a critical signal that feeds into a reinforcement learning and optimization cycle. Plano makes this trivial by sampling hyper-rich information traces from live production agentic interactions so that you can improve agent performance faster.",
|
||||
image: "/Telemetry.svg"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: "SHIP CONFIDENTLY",
|
||||
title: "Centrally apply guardrail policies",
|
||||
description: "Plano comes built-in with a state-of-the-art guardrail model you can use for things like jailbreak detection. But you can easily extend those capabilities via plano's agent filter chain to apply custom policy checks in a centralized way and keep users engaged on topics relevant to your requirements.",
|
||||
image: "/ShipConfidently.svg"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
category: "OPTIMIZE CONTINUOUSLY",
|
||||
title: "Learn and improve",
|
||||
description: "Built-in analytics and feedback loops help your agents get smarter over time. Track performance, identify bottlenecks, and optimize workflows with real-time insights and automated improvements.",
|
||||
category: "SCALABLE ARCHITECTURE",
|
||||
title: "Protocol-Native Infrastructure",
|
||||
description: "Plano's sidecar deployment model avoids library-based abstractions - operating as a protocol-native data plane that integrates seamlessly with your existing agents via agentic APIs (like v1/responses). This decouples your core agent logic from plumbing concerns - run it alongside any framework without code changes, vendor lock-in, or performance overhead.",
|
||||
image: "/Contextual.svg"
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,34 +1,47 @@
|
|||
import React from "react";
|
||||
import { AsciiDiagram } from "@/components/AsciiDiagram";
|
||||
import { diagrams } from "@/data/diagrams";
|
||||
import Image from "next/image";
|
||||
|
||||
export function IntroSection() {
|
||||
return (
|
||||
<section className="relative bg-[#1a1a1a] text-white py-20 px-6 lg:px-[102px]">
|
||||
<div className="max-w-324 mx-auto">
|
||||
<div className="flex flex-col lg:flex-row gap-12 items-start">
|
||||
<div className="flex flex-col lg:flex-row gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
<div className="flex-1">
|
||||
{/* Heading */}
|
||||
<p className="font-mono font-bold text-primary-light text-xl tracking-[1.92px]! mb-4 leading-[1.102]">
|
||||
WHY PLANO?
|
||||
</p>
|
||||
<h2 className="font-sans font-medium tracking-[-1.92px]! text-[#9797ea] text-4xl leading-[1.102] mb-6 max-w-[633px]">
|
||||
Go beyond AI nascent demos
|
||||
Ship prototypes to production
|
||||
<span className="italic">—fast.</span>
|
||||
</h2>
|
||||
|
||||
{/* Body Text */}
|
||||
<div className="font-mono tracking-[-0.96px]! text-white text-lg max-w-[713px]">
|
||||
<p className="mb-0 leading-8">
|
||||
Plano is the infrastructure layer for building, scaling, and routing AI agents. It sits between your app and your models, enforcing safety guardrails, orchestrating multi-agent workflows, and unifying access across large language models.
|
||||
</p>
|
||||
<br />
|
||||
<p className="mb-0 leading-8">
|
||||
Developers use Plano to build faster, platform teams use it to unify governance, and AI leads use it to run continuously learning systems that stay aligned, safe, and performant. From SaaS backends to distributed ecosystems, Plano turns raw agent logic into something you can deploy, monitor, and evolve in production.
|
||||
Plano is a framework-friendly proxy server and dataplane for agents,
|
||||
deployed as a sidecar. Plano handles the critical plumbing work in AI
|
||||
like agent routing and orchestration, comprehensive traces for agentic
|
||||
interactions, guardrail hooks, unified APIs for LLMs — so <strong> developers </strong>
|
||||
can focus more on modeling workflows, <strong> product teams </strong> can accelerate
|
||||
feedback loops for reinforcement learning and <strong>engineering teams</strong> can
|
||||
standardize policies and access controls across every agent and LLM for
|
||||
safer, more reliable scaling.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Diagram */}
|
||||
<div className="lg:w-[660px] relative flex-shrink-0">
|
||||
<AsciiDiagram content={diagrams.infrastructureLayer} className="max-w-none mx-0" />
|
||||
<div className="flex-1 relative w-full">
|
||||
<Image
|
||||
src="/NetworkPathDiagram.svg"
|
||||
alt="Network Path Diagram"
|
||||
width={800}
|
||||
height={600}
|
||||
className="w-full h-auto"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import { Button } from "./ui/button";
|
|||
import { cn } from "@/lib/utils";
|
||||
|
||||
const navItems = [
|
||||
{ href: "/start", label: "start locally" },
|
||||
{ href: "/docs", label: "docs" },
|
||||
{ href: "/pricing", label: "pricing" },
|
||||
{ href: "/model-research", label: "models research" },
|
||||
{ href: "/blog", label: "blog" },
|
||||
{ href: "/why", label: "why plano?" },
|
||||
];
|
||||
|
||||
export function Navbar() {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ export function UnlockPotentialSection({
|
|||
<section className={`relative py-24 px-6 lg:px-[102px] ${backgroundClass} ${className}`}>
|
||||
<div className="max-w-[81rem] mx-auto">
|
||||
<div className="max-w-4xl">
|
||||
<h2 className={`font-sans font-normal text-3xl lg:text-4xl tracking-[-2.88px]! ${textColor} leading-[1.03] mb-8`}>
|
||||
Unlock the full potential of your applications with Plano.
|
||||
<h2 className={`font-sans font-normal text-3xl lg:text-4xl tracking-[-2.88px]! ${textColor} leading-[1.4] mb-8`}>
|
||||
Focus on prompting, not plumbing<br />
|
||||
Build with <strong className="font-medium text-primary">plano</strong>, get started in less than a minute.
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-5">
|
||||
|
|
|
|||
|
|
@ -1,35 +1,60 @@
|
|||
import React from "react";
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { ArrowRightIcon } from "lucide-react";
|
||||
import { Button } from "./ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "./ui/dialog";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
const useCasesData = [
|
||||
interface UseCase {
|
||||
id: number;
|
||||
category: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
fullContent: string;
|
||||
}
|
||||
|
||||
const useCasesData: UseCase[] = [
|
||||
{
|
||||
id: 1,
|
||||
category: "FROM SAAS TO AGENTS",
|
||||
title: "Transform software into active agents",
|
||||
link: "Learn more"
|
||||
category: "AGENT ORCHESTRATION",
|
||||
title: "Multi-agent systems without framework lock-in",
|
||||
summary: "Seamless routing and orchestration for complex agent interactions",
|
||||
fullContent: "Plano manages agent routing and orchestration without framework dependencies, allowing seamless multi-agent interactions. This is ideal for building complex systems like automated customer support or data processing pipelines, where agents hand off tasks efficiently to deliver end-to-end solutions faster."
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category: "AGENTIC TASKS",
|
||||
title: "Train models through live feedback",
|
||||
link: "Learn more"
|
||||
category: "CONTEXT ENGINEERING",
|
||||
title: "Reusable filters for smarter agents",
|
||||
summary: "Inject data, reformulate queries, and enforce policies efficiently",
|
||||
fullContent: "Plano's filter chain encourages reuse and decoupling for context engineering tasks like injecting data, reformulating queries, and enforcing policy before calls reach an agent or LLM. This means faster debugging, cleaner architecture, and more accurate, on-policy agents —without bespoke glue code."
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category: "AGENTIC ROUTING",
|
||||
title: "Route logic across smart agents",
|
||||
link: "Learn more"
|
||||
category: "REINFORCEMENT LEARNING",
|
||||
title: "Production signals for continuous improvement",
|
||||
summary: "Capture rich traces to accelerate training and refinement",
|
||||
fullContent: "Plano captures hyper-rich tracing and log samples from production traffic, feeding into reinforcement learning and fine-tuning cycles. This accelerates iteration in areas like recommendation engines, helping teams quickly identify failures, refine prompts, and boost agent effectiveness based on real-user signals."
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: "SAAS INTEGRATIONS",
|
||||
title: "Design networks of intelligent agents",
|
||||
link: "Learn more"
|
||||
category: "SECURITY",
|
||||
title: "Built-in guardrails and centralized policies",
|
||||
summary: "Safe scaling with jailbreak detection and access controls",
|
||||
fullContent: "With built-in guardrails, centralized policies, and access controls, Plano ensures safe scaling across LLMs, detecting issues like jailbreak attempts. This is critical for deployments in regulated fields like finance or healthcare, and minimizing risks while standardizing reliability and security of agents."
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
category: "ON-PREMISES",
|
||||
title: "Full data control in regulated environments",
|
||||
summary: "Deploy on private infrastructure without compromising features",
|
||||
fullContent: "Plano's lightweight sidecar model deploys effortlessly on your private infrastructure, empowering teams in regulated sectors to maintain full data control while benefiting from unified LLM access, custom filter chains, and production-grade tracing—without compromising on security or scalability."
|
||||
}
|
||||
];
|
||||
|
||||
export function UseCasesSection() {
|
||||
const [selectedUseCase, setSelectedUseCase] = useState<UseCase | null>(null);
|
||||
|
||||
return (
|
||||
<section className="relative py-24 px-6 lg:px-[102px]">
|
||||
<div className="max-w-[81rem] mx-auto">
|
||||
|
|
@ -53,37 +78,74 @@ export function UseCasesSection() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* 4 Box Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{/* 5 Card Grid - Horizontal Row */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
|
||||
{useCasesData.map((useCase) => (
|
||||
<div
|
||||
<motion.div
|
||||
key={useCase.id}
|
||||
className="bg-gradient-to-b from-[rgba(177,184,255,0.16)] to-[rgba(17,28,132,0.035)] border-2 border-[rgba(171,178,250,0.27)] rounded-md p-8 h-87 flex flex-col justify-between"
|
||||
whileHover={{ y: -4 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="bg-gradient-to-b from-[rgba(177,184,255,0.16)] to-[rgba(17,28,132,0.035)] border-2 border-[rgba(171,178,250,0.27)] rounded-md p-8 h-90 flex flex-col justify-between cursor-pointer"
|
||||
onClick={() => setSelectedUseCase(useCase)}
|
||||
>
|
||||
{/* Category */}
|
||||
<div className="mb-6">
|
||||
<p className="font-mono font-bold text-[#2a3178] text-lg tracking-[1.92px]! w-38 mb-6">
|
||||
<p className="font-mono font-bold text-[#2a3178] text-base tracking-[1.92px]! mb-4">
|
||||
{useCase.category}
|
||||
</p>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="font-sans font-medium text-black text-2xl lg:text-3xl tracking-[-1.2px]! leading-[1.102]">
|
||||
<h3 className="font-sans font-medium text-black text-xl lg:text-2xl tracking-[-1.2px]! leading-[1.102]">
|
||||
{useCase.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Learn More Link */}
|
||||
<div className="mt-auto">
|
||||
<button className="group flex items-center gap-2 font-mono font-bold text-[var(--primary)] text-lg tracking-[1.92px]! leading-[1.45] hover:text-[var(--primary-dark)] transition-colors">
|
||||
<button className="group flex items-center gap-2 font-mono font-bold text-[var(--primary)] text-base tracking-[1.92px]! leading-[1.45] hover:text-[var(--primary-dark)] transition-colors">
|
||||
LEARN MORE
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
<ArrowRightIcon className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Modal */}
|
||||
<Dialog open={selectedUseCase !== null} onOpenChange={(open) => !open && setSelectedUseCase(null)}>
|
||||
<AnimatePresence>
|
||||
{selectedUseCase && (
|
||||
<DialogContent className="max-w-2xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: 10 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: 10 }}
|
||||
transition={{ duration: 0.2, ease: "easeOut" }}
|
||||
>
|
||||
<DialogHeader>
|
||||
<div className="mb-4">
|
||||
<p className="font-mono font-bold text-[#2a3178] text-sm tracking-[1.62px]! mb-3">
|
||||
USE CASE
|
||||
</p>
|
||||
<DialogTitle className="font-sans font-medium text-3xl tracking-[-1.5px]! text-black leading-[1.1]">
|
||||
{selectedUseCase.category}
|
||||
</DialogTitle>
|
||||
</div>
|
||||
<DialogDescription className="font-mono text-[#494949] text-base leading-relaxed pt-2">
|
||||
{selectedUseCase.fullContent}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="mt-6 flex justify-end">
|
||||
<Button onClick={() => setSelectedUseCase(null)}>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</DialogContent>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Dialog>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
143
www/src/components/ui/dialog.tsx
Normal file
143
www/src/components/ui/dialog.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Dialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue