Segundo vistaso
This commit is contained in:
616
package-lock.json
generated
616
package-lock.json
generated
@@ -8,9 +8,12 @@
|
|||||||
"name": "landing-page",
|
"name": "landing-page",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tailwindcss/postcss": "^4.3.0",
|
||||||
"next": "16.2.6",
|
"next": "16.2.6",
|
||||||
|
"postcss": "^8.5.15",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4",
|
||||||
|
"tailwindcss": "^4.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
@@ -21,6 +24,18 @@
|
|||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@alloc/quick-lru": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.29.7",
|
"version": "7.29.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
||||||
@@ -265,7 +280,6 @@
|
|||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -287,7 +301,6 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -974,7 +987,6 @@
|
|||||||
"version": "0.3.13",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
@@ -985,7 +997,6 @@
|
|||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
@@ -996,7 +1007,6 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -1006,14 +1016,12 @@
|
|||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.31",
|
"version": "0.3.31",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
@@ -1024,7 +1032,6 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
||||||
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
|
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1247,11 +1254,266 @@
|
|||||||
"tslib": "^2.8.0"
|
"tslib": "^2.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/node": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/remapping": "^2.3.5",
|
||||||
|
"enhanced-resolve": "^5.21.0",
|
||||||
|
"jiti": "^2.6.1",
|
||||||
|
"lightningcss": "1.32.0",
|
||||||
|
"magic-string": "^0.30.21",
|
||||||
|
"source-map-js": "^1.2.1",
|
||||||
|
"tailwindcss": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@tailwindcss/oxide-android-arm64": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-darwin-arm64": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-darwin-x64": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-freebsd-x64": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-linux-arm64-gnu": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-linux-arm64-musl": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-linux-x64-gnu": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-linux-x64-musl": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-win32-arm64-msvc": "4.3.0",
|
||||||
|
"@tailwindcss/oxide-win32-x64-msvc": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==",
|
||||||
|
"bundleDependencies": [
|
||||||
|
"@napi-rs/wasm-runtime",
|
||||||
|
"@emnapi/core",
|
||||||
|
"@emnapi/runtime",
|
||||||
|
"@tybys/wasm-util",
|
||||||
|
"@emnapi/wasi-threads",
|
||||||
|
"tslib"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"wasm32"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/core": "^1.10.0",
|
||||||
|
"@emnapi/runtime": "^1.10.0",
|
||||||
|
"@emnapi/wasi-threads": "^1.2.1",
|
||||||
|
"@napi-rs/wasm-runtime": "^1.1.4",
|
||||||
|
"@tybys/wasm-util": "^0.10.1",
|
||||||
|
"tslib": "^2.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/postcss": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
|
"@tailwindcss/node": "4.3.0",
|
||||||
|
"@tailwindcss/oxide": "4.3.0",
|
||||||
|
"postcss": "^8.5.10",
|
||||||
|
"tailwindcss": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.2",
|
"version": "0.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
|
||||||
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
|
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2566,7 +2828,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -2613,6 +2874,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/enhanced-resolve": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"tapable": "^2.3.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.24.2",
|
"version": "1.24.2",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",
|
||||||
@@ -3547,6 +3821,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/has-bigints": {
|
"node_modules/has-bigints": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
|
||||||
@@ -4157,6 +4437,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jiti": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -4284,6 +4573,255 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lightningcss": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"lightningcss-android-arm64": "1.32.0",
|
||||||
|
"lightningcss-darwin-arm64": "1.32.0",
|
||||||
|
"lightningcss-darwin-x64": "1.32.0",
|
||||||
|
"lightningcss-freebsd-x64": "1.32.0",
|
||||||
|
"lightningcss-linux-arm-gnueabihf": "1.32.0",
|
||||||
|
"lightningcss-linux-arm64-gnu": "1.32.0",
|
||||||
|
"lightningcss-linux-arm64-musl": "1.32.0",
|
||||||
|
"lightningcss-linux-x64-gnu": "1.32.0",
|
||||||
|
"lightningcss-linux-x64-musl": "1.32.0",
|
||||||
|
"lightningcss-win32-arm64-msvc": "1.32.0",
|
||||||
|
"lightningcss-win32-x64-msvc": "1.32.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-android-arm64": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-darwin-arm64": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-darwin-x64": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-freebsd-x64": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-linux-arm64-musl": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-linux-x64-gnu": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-linux-x64-musl": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-win32-x64-msvc": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||||
@@ -4330,6 +4868,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.30.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -4805,6 +5352,34 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.5.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.12",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
@@ -5501,6 +6076,25 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwindcss": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tapable": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.16",
|
"version": "0.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||||
|
|||||||
@@ -9,9 +9,12 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tailwindcss/postcss": "^4.3.0",
|
||||||
"next": "16.2.6",
|
"next": "16.2.6",
|
||||||
|
"postcss": "^8.5.15",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4",
|
||||||
|
"tailwindcss": "^4.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|||||||
5
postcss.config.mjs
Normal file
5
postcss.config.mjs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
'@tailwindcss/postcss': {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
|
@import "tailwindcss";
|
||||||
|
|
||||||
/* ============================================
|
@theme {
|
||||||
DESIGN SYSTEM TOKENS
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
/* Colors */
|
/* Colors */
|
||||||
--color-black: #0a0a0a;
|
--color-black: #0a0a0a;
|
||||||
--color-dark: #111111;
|
--color-dark: #111111;
|
||||||
@@ -11,6 +8,7 @@
|
|||||||
--color-gray-800: #2d2d2d;
|
--color-gray-800: #2d2d2d;
|
||||||
--color-gray-600: #555555;
|
--color-gray-600: #555555;
|
||||||
--color-gray-400: #888888;
|
--color-gray-400: #888888;
|
||||||
|
--color-gray-300: #d1d1d1;
|
||||||
--color-gray-200: #e5e5e5;
|
--color-gray-200: #e5e5e5;
|
||||||
--color-gray-100: #f5f5f5;
|
--color-gray-100: #f5f5f5;
|
||||||
--color-gray-50: #fafafa;
|
--color-gray-50: #fafafa;
|
||||||
@@ -25,290 +23,91 @@
|
|||||||
--color-success: #00c853;
|
--color-success: #00c853;
|
||||||
--color-error: #ff3b3b;
|
--color-error: #ff3b3b;
|
||||||
|
|
||||||
/* Typography */
|
/* Fonts */
|
||||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
|
||||||
--text-xs: 0.75rem;
|
|
||||||
--text-sm: 0.875rem;
|
|
||||||
--text-base: 1rem;
|
|
||||||
--text-lg: 1.125rem;
|
|
||||||
--text-xl: 1.25rem;
|
|
||||||
--text-2xl: 1.5rem;
|
|
||||||
--text-3xl: 1.875rem;
|
|
||||||
--text-4xl: 2.25rem;
|
|
||||||
--text-5xl: 3rem;
|
|
||||||
--text-6xl: 3.75rem;
|
|
||||||
--text-7xl: 4.5rem;
|
|
||||||
|
|
||||||
/* Spacing */
|
|
||||||
--space-1: 0.25rem;
|
|
||||||
--space-2: 0.5rem;
|
|
||||||
--space-3: 0.75rem;
|
|
||||||
--space-4: 1rem;
|
|
||||||
--space-6: 1.5rem;
|
|
||||||
--space-8: 2rem;
|
|
||||||
--space-10: 2.5rem;
|
|
||||||
--space-12: 3rem;
|
|
||||||
--space-16: 4rem;
|
|
||||||
--space-20: 5rem;
|
|
||||||
--space-24: 6rem;
|
|
||||||
--space-32: 8rem;
|
|
||||||
|
|
||||||
/* Border Radius */
|
|
||||||
--radius-sm: 4px;
|
|
||||||
--radius-md: 8px;
|
|
||||||
--radius-lg: 12px;
|
|
||||||
--radius-xl: 16px;
|
|
||||||
--radius-2xl: 24px;
|
|
||||||
--radius-full: 9999px;
|
|
||||||
|
|
||||||
/* Shadows */
|
|
||||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
||||||
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
||||||
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.10);
|
|
||||||
--shadow-xl: 0 24px 64px rgba(0, 0, 0, 0.12);
|
|
||||||
|
|
||||||
/* Transitions */
|
/* Transitions */
|
||||||
--transition-fast: 150ms ease;
|
--transition-fast: 150ms ease;
|
||||||
--transition-base: 250ms ease;
|
--transition-base: 250ms ease;
|
||||||
--transition-slow: 400ms ease;
|
--transition-slow: 400ms ease;
|
||||||
|
|
||||||
/* Max widths */
|
|
||||||
--max-width-sm: 640px;
|
|
||||||
--max-width-md: 768px;
|
|
||||||
--max-width-lg: 1024px;
|
|
||||||
--max-width-xl: 1280px;
|
|
||||||
--max-width-2xl: 1440px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
@layer base {
|
||||||
RESET & BASE
|
*, *::before, *::after {
|
||||||
============================================ */
|
box-sizing: border-box;
|
||||||
*, *::before, *::after {
|
margin: 0;
|
||||||
box-sizing: border-box;
|
padding: 0;
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
font-size: var(--text-base);
|
|
||||||
color: var(--color-dark);
|
|
||||||
background-color: var(--color-white);
|
|
||||||
line-height: 1.6;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
img, video {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, textarea, select {
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
UTILITIES
|
|
||||||
============================================ */
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: var(--max-width-xl);
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 var(--space-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
padding: var(--space-24) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
.btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
padding: var(--space-3) var(--space-6);
|
|
||||||
font-size: var(--text-base);
|
|
||||||
font-weight: 600;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
transition: all var(--transition-base);
|
|
||||||
letter-spacing: -0.01em;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
border: 2px solid var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background: var(--color-gray-900);
|
|
||||||
border-color: var(--color-gray-900);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background: transparent;
|
|
||||||
color: var(--color-dark);
|
|
||||||
border: 2px solid var(--color-gray-200);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
border-color: var(--color-dark);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-accent {
|
|
||||||
background: var(--color-accent);
|
|
||||||
color: var(--color-white);
|
|
||||||
border: 2px solid var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-accent:hover {
|
|
||||||
background: var(--color-accent-dark);
|
|
||||||
border-color: var(--color-accent-dark);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 102, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-lg {
|
|
||||||
padding: var(--space-4) var(--space-8);
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badges */
|
|
||||||
.badge {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
padding: var(--space-2) var(--space-3);
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
border-radius: var(--radius-full);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-dark {
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-accent {
|
|
||||||
background: var(--color-accent-light);
|
|
||||||
color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SCROLLBAR
|
|
||||||
============================================ */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background: var(--color-gray-100);
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--color-gray-400);
|
|
||||||
border-radius: var(--radius-full);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
ANIMATIONS
|
|
||||||
============================================ */
|
|
||||||
@keyframes fadeInUp {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(24px);
|
|
||||||
}
|
}
|
||||||
to {
|
|
||||||
opacity: 1;
|
html {
|
||||||
transform: translateY(0);
|
scroll-behavior: smooth;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--color-dark);
|
||||||
|
background-color: var(--color-white);
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--color-gray-100);
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-gray-400);
|
||||||
|
border-radius: 9999px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeIn {
|
@layer components {
|
||||||
from { opacity: 0; }
|
/* Layout */
|
||||||
to { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideInRight {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: 0.5; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes scaleIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-fadeInUp {
|
|
||||||
animation: fadeInUp 0.6s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-fadeIn {
|
|
||||||
animation: fadeIn 0.5s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
RESPONSIVE
|
|
||||||
============================================ */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0 var(--space-4);
|
@apply w-full max-w-7xl mx-auto px-6 md:px-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
padding: var(--space-16) 0;
|
@apply py-16 md:py-24;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.btn {
|
||||||
|
@apply inline-flex items-center justify-center gap-2 px-6 py-3 text-base font-semibold rounded-lg transition-all duration-250 ease-out whitespace-nowrap cursor-pointer tracking-tight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
@apply bg-black text-white border-2 border-black hover:bg-gray-900 hover:border-gray-900 hover:-translate-y-[1px] hover:shadow-[0_8px_32px_rgba(0,0,0,0.10)];
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply bg-transparent text-dark border-2 border-gray-200 hover:border-dark hover:-translate-y-[1px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accent {
|
||||||
|
@apply bg-accent text-white border-2 border-accent hover:bg-accent-dark hover:border-accent-dark hover:-translate-y-[1px] hover:shadow-[0_8px_24px_rgba(0,102,255,0.3)];
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lg {
|
||||||
|
@apply px-8 py-4 text-lg rounded-xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badges */
|
||||||
|
.badge {
|
||||||
|
@apply inline-flex items-center gap-2 px-3 py-2 text-xs font-semibold uppercase tracking-widest rounded-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-dark {
|
||||||
|
@apply bg-black text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-accent {
|
||||||
|
@apply bg-accent-light text-accent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,367 +0,0 @@
|
|||||||
.contactSection {
|
|
||||||
background: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Layout */
|
|
||||||
.layout {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: var(--space-16);
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reveal */
|
|
||||||
.reveal {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(24px);
|
|
||||||
transition: opacity 0.65s ease, transform 0.65s ease;
|
|
||||||
}
|
|
||||||
.reveal.visible {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Info panel */
|
|
||||||
.infoPanel {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-6);
|
|
||||||
position: sticky;
|
|
||||||
top: calc(72px + var(--space-8));
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: clamp(var(--text-3xl), 5vw, var(--text-5xl));
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
line-height: 1.05;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Contact details */
|
|
||||||
.contactDetails {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
padding: var(--space-6);
|
|
||||||
background: var(--color-gray-50);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contactItem {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contactIcon {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contactLabel {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.06em;
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contactValue {
|
|
||||||
font-size: var(--text-base);
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Testimonial */
|
|
||||||
.testimonial {
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
padding: var(--space-6);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.testimonialText {
|
|
||||||
font-size: var(--text-base);
|
|
||||||
line-height: 1.65;
|
|
||||||
color: rgba(255,255,255,0.85);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.testimonialAuthor {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.authorAvatar {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: rgba(255,255,255,0.15);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.02em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.authorName {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.authorRole {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: rgba(255,255,255,0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Form panel */
|
|
||||||
.formPanel {
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-2xl);
|
|
||||||
padding: var(--space-10);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Form */
|
|
||||||
.form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.formHeader {
|
|
||||||
margin-bottom: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.formTitle {
|
|
||||||
font-size: var(--text-2xl);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.03em;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.formSubtitle {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
margin-top: var(--space-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Row */
|
|
||||||
.row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Field */
|
|
||||||
.fieldGroup {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
.optional {
|
|
||||||
font-weight: 400;
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inputs */
|
|
||||||
.input,
|
|
||||||
.textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: var(--space-3) var(--space-4);
|
|
||||||
font-size: var(--text-base);
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
color: var(--color-dark);
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1.5px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input::placeholder,
|
|
||||||
.textarea::placeholder {
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus,
|
|
||||||
.textarea:focus {
|
|
||||||
border-color: var(--color-black);
|
|
||||||
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputError {
|
|
||||||
border-color: var(--color-error) !important;
|
|
||||||
box-shadow: 0 0 0 3px rgba(255, 59, 59, 0.08) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textarea {
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textareaFooter {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Error */
|
|
||||||
.errorMsg {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--color-error);
|
|
||||||
font-weight: 500;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.charCount {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Submit */
|
|
||||||
.submitBtn {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.submitBtn:disabled {
|
|
||||||
opacity: 0.7;
|
|
||||||
cursor: not-allowed;
|
|
||||||
transform: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Spinner */
|
|
||||||
@keyframes spin {
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
border: 2px solid rgba(255,255,255,0.3);
|
|
||||||
border-top-color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 0.7s linear infinite;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Privacy note */
|
|
||||||
.privacyNote {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
text-align: center;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.privacyLink {
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 2px;
|
|
||||||
transition: color var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.privacyLink:hover {
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Success state */
|
|
||||||
.successState {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
gap: var(--space-4);
|
|
||||||
padding: var(--space-16) var(--space-8);
|
|
||||||
animation: scaleIn 0.4s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.successIcon {
|
|
||||||
width: 72px;
|
|
||||||
height: 72px;
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.successTitle {
|
|
||||||
font-size: var(--text-2xl);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.03em;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.successDesc {
|
|
||||||
font-size: var(--text-base);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
max-width: 320px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.layout {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: var(--space-12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.infoPanel {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
|
|
||||||
.formPanel {
|
|
||||||
padding: var(--space-8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.row {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.formPanel {
|
|
||||||
padding: var(--space-6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useRef, useEffect, FormEvent } from 'react';
|
import { useState, useRef, useEffect, FormEvent } from 'react';
|
||||||
import styles from './ContactForm.module.css';
|
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -53,12 +52,15 @@ export default function ContactForm() {
|
|||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting) entry.target.classList.add(styles.visible);
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('opacity-100', 'translate-y-0');
|
||||||
|
entry.target.classList.remove('opacity-0', 'translate-y-6');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ threshold: 0.1 }
|
{ threshold: 0.1 }
|
||||||
);
|
);
|
||||||
const elements = sectionRef.current?.querySelectorAll(`.${styles.reveal}`);
|
const elements = sectionRef.current?.querySelectorAll('.reveal');
|
||||||
elements?.forEach((el) => observer.observe(el));
|
elements?.forEach((el) => observer.observe(el));
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -115,94 +117,121 @@ export default function ContactForm() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`section ${styles.contactSection}`}
|
className="section bg-white"
|
||||||
id="contact"
|
id="contact"
|
||||||
ref={sectionRef}
|
ref={sectionRef}
|
||||||
aria-labelledby="contact-heading"
|
aria-labelledby="contact-heading"
|
||||||
>
|
>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className={styles.layout}>
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 lg:gap-16 items-start">
|
||||||
{/* Left info panel */}
|
{/* Left info panel */}
|
||||||
<div className={`${styles.reveal} ${styles.infoPanel}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out flex flex-col gap-6 lg:sticky lg:top-[104px]">
|
||||||
<span className="badge badge-dark">Contacto</span>
|
<div className="mb-2">
|
||||||
<h2 id="contact-heading" className={styles.title}>
|
<span className="badge badge-dark">Contacto</span>
|
||||||
|
</div>
|
||||||
|
<h2
|
||||||
|
id="contact-heading"
|
||||||
|
className="text-[clamp(1.875rem,5vw,3rem)] font-black tracking-[-0.04em] leading-[1.05] text-black"
|
||||||
|
>
|
||||||
Hablemos de
|
Hablemos de
|
||||||
<br />
|
<br />
|
||||||
tu negocio
|
tu negocio
|
||||||
</h2>
|
</h2>
|
||||||
<p className={styles.subtitle}>
|
<p className="text-lg text-gray-600 leading-relaxed">
|
||||||
Cuéntanos qué necesitas. Nuestro equipo responderá en menos de 24 horas
|
Cuéntanos qué necesitas. Nuestro equipo responderá en menos de 24 horas
|
||||||
con una propuesta personalizada.
|
con una propuesta personalizada.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Contact details */}
|
{/* Contact details */}
|
||||||
<div className={styles.contactDetails}>
|
<div className="flex flex-col gap-4 p-6 bg-gray-50 border border-gray-200 rounded-xl">
|
||||||
<div className={styles.contactItem}>
|
<div className="flex items-center gap-4">
|
||||||
<div className={styles.contactIcon}>
|
<div className="w-10 h-10 bg-white border border-gray-200 rounded-lg flex items-center justify-center shrink-0 text-black">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" stroke="currentColor" strokeWidth="2" />
|
<path
|
||||||
|
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
<polyline points="22,6 12,13 2,6" stroke="currentColor" strokeWidth="2" />
|
<polyline points="22,6 12,13 2,6" stroke="currentColor" strokeWidth="2" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.contactLabel}>Email</div>
|
<div className="text-xs font-semibold uppercase tracking-widest text-gray-400">Email</div>
|
||||||
<div className={styles.contactValue}>hola@flowsync.io</div>
|
<div className="text-base font-semibold text-black">hola@flowsync.io</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.contactItem}>
|
<div className="flex items-center gap-4">
|
||||||
<div className={styles.contactIcon}>
|
<div className="w-10 h-10 bg-white border border-gray-200 rounded-lg flex items-center justify-center shrink-0 text-black">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.62a19.79 19.79 0 01-3.07-8.63A2 2 0 012.18 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.91 7.91a16 16 0 006.18 6.18l1.28-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z" stroke="currentColor" strokeWidth="2" />
|
<path
|
||||||
|
d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.62a19.79 19.79 0 01-3.07-8.63A2 2 0 012.18 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.91 7.91a16 16 0 006.18 6.18l1.28-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.contactLabel}>Teléfono</div>
|
<div className="text-xs font-semibold uppercase tracking-widest text-gray-400">Teléfono</div>
|
||||||
<div className={styles.contactValue}>+1 (800) 123-4567</div>
|
<div className="text-base font-semibold text-black">+1 (800) 123-4567</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.contactItem}>
|
<div className="flex items-center gap-4">
|
||||||
<div className={styles.contactIcon}>
|
<div className="w-10 h-10 bg-white border border-gray-200 rounded-lg flex items-center justify-center shrink-0 text-black">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
|
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
|
||||||
<polyline points="12 6 12 12 16 14" stroke="currentColor" strokeWidth="2" />
|
<polyline points="12 6 12 12 16 14" stroke="currentColor" strokeWidth="2" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.contactLabel}>Respuesta</div>
|
<div className="text-xs font-semibold uppercase tracking-widest text-gray-400">Respuesta</div>
|
||||||
<div className={styles.contactValue}>Menos de 24 horas</div>
|
<div className="text-base font-semibold text-black">Menos de 24 horas</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Testimonial */}
|
{/* Testimonial */}
|
||||||
<blockquote className={styles.testimonial}>
|
<blockquote className="bg-black text-white rounded-xl p-6 flex flex-col gap-4">
|
||||||
<p className={styles.testimonialText}>
|
<p className="text-base leading-relaxed text-white/85 italic">
|
||||||
“FlowSync transformó la forma en que colaboramos. Lo que antes
|
“FlowSync transformó la forma en que colaboramos. Lo que antes
|
||||||
tomaba días, ahora lo hacemos en horas.”
|
tomaba días, ahora lo hacemos en horas.”
|
||||||
</p>
|
</p>
|
||||||
<footer className={styles.testimonialAuthor}>
|
<footer className="flex items-center gap-3">
|
||||||
<div className={styles.authorAvatar}>MR</div>
|
<div className="w-10 h-10 bg-white/15 rounded-full flex items-center justify-center text-xs font-bold tracking-wider shrink-0">
|
||||||
|
MR
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.authorName}>María Rodríguez</div>
|
<div className="text-sm font-bold">María Rodríguez</div>
|
||||||
<div className={styles.authorRole}>CTO en TechLatam</div>
|
<div className="text-xs text-white/50">CTO en TechLatam</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Form panel */}
|
{/* Form panel */}
|
||||||
<div className={`${styles.reveal} ${styles.formPanel}`} style={{ transitionDelay: '0.15s' }}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-150 bg-white border border-gray-200 rounded-2xl p-10 max-sm:p-6 shadow-lg">
|
||||||
{status === 'success' ? (
|
{status === 'success' ? (
|
||||||
<div className={styles.successState} role="alert" aria-live="polite">
|
<div
|
||||||
<div className={styles.successIcon}>
|
className="flex flex-col items-center justify-center text-center gap-4 py-16 px-8 animate-scaleIn"
|
||||||
|
role="alert"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<div className="w-18 h-18 bg-black text-white rounded-full flex items-center justify-center mb-2">
|
||||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" aria-hidden="true">
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" aria-hidden="true">
|
||||||
<path d="M6 16l7 7L26 9" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
|
d="M6 16l7 7L26 9"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="3"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className={styles.successTitle}>¡Mensaje enviado!</h3>
|
<h3 className="text-2xl font-extrabold tracking-tight text-black">
|
||||||
<p className={styles.successDesc}>
|
¡Mensaje enviado!
|
||||||
|
</h3>
|
||||||
|
<p className="text-base text-gray-600 max-w-[320px] leading-relaxed mb-4">
|
||||||
Gracias por contactarnos. Nuestro equipo te responderá en menos de 24 horas.
|
Gracias por contactarnos. Nuestro equipo te responderá en menos de 24 horas.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
@@ -215,28 +244,36 @@ export default function ContactForm() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<form
|
<form
|
||||||
className={styles.form}
|
className="flex flex-col gap-5"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
noValidate
|
noValidate
|
||||||
aria-label="Formulario de contacto"
|
aria-label="Formulario de contacto"
|
||||||
id="contact-form"
|
id="contact-form"
|
||||||
>
|
>
|
||||||
<div className={styles.formHeader}>
|
<div className="mb-2">
|
||||||
<h3 className={styles.formTitle}>Envíanos un mensaje</h3>
|
<h3 className="text-2xl font-extrabold tracking-tight text-black">
|
||||||
<p className={styles.formSubtitle}>Todos los campos marcados con * son requeridos</p>
|
Envíanos un mensaje
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-400 mt-1">
|
||||||
|
Todos los campos marcados con * son requeridos
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Row: Name + Email */}
|
{/* Row: Name + Email */}
|
||||||
<div className={styles.row}>
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div className={styles.fieldGroup}>
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="contact-name" className={styles.label}>
|
<label htmlFor="contact-name" className="text-sm font-semibold text-dark">
|
||||||
Nombre completo <span className={styles.required}>*</span>
|
Nombre completo <span className="text-error">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="contact-name"
|
id="contact-name"
|
||||||
name="name"
|
name="name"
|
||||||
type="text"
|
type="text"
|
||||||
className={`${styles.input} ${errors.name && touched.name ? styles.inputError : ''}`}
|
className={`w-full px-4 py-3 text-base font-sans text-dark bg-white border-[1.5px] rounded-lg transition-all duration-150 outline-none placeholder:text-gray-400 focus:border-black focus:shadow-[0_0_0_3px_rgba(0,0,0,0.06)] ${
|
||||||
|
errors.name && touched.name
|
||||||
|
? 'border-error shadow-[0_0_0_3px_rgba(255,59,59,0.08)]'
|
||||||
|
: 'border-gray-200'
|
||||||
|
}`}
|
||||||
placeholder="Juan García"
|
placeholder="Juan García"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -247,21 +284,25 @@ export default function ContactForm() {
|
|||||||
aria-invalid={!!(errors.name && touched.name)}
|
aria-invalid={!!(errors.name && touched.name)}
|
||||||
/>
|
/>
|
||||||
{errors.name && touched.name && (
|
{errors.name && touched.name && (
|
||||||
<span id="name-error" className={styles.errorMsg} role="alert">
|
<span id="name-error" className="text-xs text-error font-medium flex items-center gap-1" role="alert">
|
||||||
{errors.name}
|
{errors.name}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.fieldGroup}>
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="contact-email" className={styles.label}>
|
<label htmlFor="contact-email" className="text-sm font-semibold text-dark">
|
||||||
Email <span className={styles.required}>*</span>
|
Email <span className="text-error">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="contact-email"
|
id="contact-email"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
className={`${styles.input} ${errors.email && touched.email ? styles.inputError : ''}`}
|
className={`w-full px-4 py-3 text-base font-sans text-dark bg-white border-[1.5px] rounded-lg transition-all duration-150 outline-none placeholder:text-gray-400 focus:border-black focus:shadow-[0_0_0_3px_rgba(0,0,0,0.06)] ${
|
||||||
|
errors.email && touched.email
|
||||||
|
? 'border-error shadow-[0_0_0_3px_rgba(255,59,59,0.08)]'
|
||||||
|
: 'border-gray-200'
|
||||||
|
}`}
|
||||||
placeholder="juan@empresa.com"
|
placeholder="juan@empresa.com"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -272,7 +313,7 @@ export default function ContactForm() {
|
|||||||
aria-invalid={!!(errors.email && touched.email)}
|
aria-invalid={!!(errors.email && touched.email)}
|
||||||
/>
|
/>
|
||||||
{errors.email && touched.email && (
|
{errors.email && touched.email && (
|
||||||
<span id="email-error" className={styles.errorMsg} role="alert">
|
<span id="email-error" className="text-xs text-error font-medium flex items-center gap-1" role="alert">
|
||||||
{errors.email}
|
{errors.email}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -280,16 +321,20 @@ export default function ContactForm() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Row: Company + Phone */}
|
{/* Row: Company + Phone */}
|
||||||
<div className={styles.row}>
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div className={styles.fieldGroup}>
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="contact-company" className={styles.label}>
|
<label htmlFor="contact-company" className="text-sm font-semibold text-dark">
|
||||||
Empresa <span className={styles.required}>*</span>
|
Empresa <span className="text-error">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="contact-company"
|
id="contact-company"
|
||||||
name="company"
|
name="company"
|
||||||
type="text"
|
type="text"
|
||||||
className={`${styles.input} ${errors.company && touched.company ? styles.inputError : ''}`}
|
className={`w-full px-4 py-3 text-base font-sans text-dark bg-white border-[1.5px] rounded-lg transition-all duration-150 outline-none placeholder:text-gray-400 focus:border-black focus:shadow-[0_0_0_3px_rgba(0,0,0,0.06)] ${
|
||||||
|
errors.company && touched.company
|
||||||
|
? 'border-error shadow-[0_0_0_3px_rgba(255,59,59,0.08)]'
|
||||||
|
: 'border-gray-200'
|
||||||
|
}`}
|
||||||
placeholder="Mi Empresa S.A."
|
placeholder="Mi Empresa S.A."
|
||||||
value={formData.company}
|
value={formData.company}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -300,22 +345,26 @@ export default function ContactForm() {
|
|||||||
aria-invalid={!!(errors.company && touched.company)}
|
aria-invalid={!!(errors.company && touched.company)}
|
||||||
/>
|
/>
|
||||||
{errors.company && touched.company && (
|
{errors.company && touched.company && (
|
||||||
<span id="company-error" className={styles.errorMsg} role="alert">
|
<span id="company-error" className="text-xs text-error font-medium flex items-center gap-1" role="alert">
|
||||||
{errors.company}
|
{errors.company}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.fieldGroup}>
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="contact-phone" className={styles.label}>
|
<label htmlFor="contact-phone" className="text-sm font-semibold text-dark">
|
||||||
Teléfono
|
Teléfono
|
||||||
<span className={styles.optional}> (opcional)</span>
|
<span className="font-normal text-gray-400"> (opcional)</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="contact-phone"
|
id="contact-phone"
|
||||||
name="phone"
|
name="phone"
|
||||||
type="tel"
|
type="tel"
|
||||||
className={`${styles.input} ${errors.phone && touched.phone ? styles.inputError : ''}`}
|
className={`w-full px-4 py-3 text-base font-sans text-dark bg-white border-[1.5px] rounded-lg transition-all duration-150 outline-none placeholder:text-gray-400 focus:border-black focus:shadow-[0_0_0_3px_rgba(0,0,0,0.06)] ${
|
||||||
|
errors.phone && touched.phone
|
||||||
|
? 'border-error shadow-[0_0_0_3px_rgba(255,59,59,0.08)]'
|
||||||
|
: 'border-gray-200'
|
||||||
|
}`}
|
||||||
placeholder="+52 55 1234 5678"
|
placeholder="+52 55 1234 5678"
|
||||||
value={formData.phone}
|
value={formData.phone}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -325,7 +374,7 @@ export default function ContactForm() {
|
|||||||
aria-invalid={!!(errors.phone && touched.phone)}
|
aria-invalid={!!(errors.phone && touched.phone)}
|
||||||
/>
|
/>
|
||||||
{errors.phone && touched.phone && (
|
{errors.phone && touched.phone && (
|
||||||
<span id="phone-error" className={styles.errorMsg} role="alert">
|
<span id="phone-error" className="text-xs text-error font-medium flex items-center gap-1" role="alert">
|
||||||
{errors.phone}
|
{errors.phone}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -333,14 +382,18 @@ export default function ContactForm() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Message */}
|
{/* Message */}
|
||||||
<div className={styles.fieldGroup}>
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="contact-message" className={styles.label}>
|
<label htmlFor="contact-message" className="text-sm font-semibold text-dark">
|
||||||
Mensaje <span className={styles.required}>*</span>
|
Mensaje <span className="text-error">*</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="contact-message"
|
id="contact-message"
|
||||||
name="message"
|
name="message"
|
||||||
className={`${styles.textarea} ${errors.message && touched.message ? styles.inputError : ''}`}
|
className={`w-full px-4 py-3 text-base font-sans text-dark bg-white border-[1.5px] rounded-lg transition-all duration-150 outline-none placeholder:text-gray-400 focus:border-black focus:shadow-[0_0_0_3px_rgba(0,0,0,0.06)] resize-y min-h-[120px] ${
|
||||||
|
errors.message && touched.message
|
||||||
|
? 'border-error shadow-[0_0_0_3px_rgba(255,59,59,0.08)]'
|
||||||
|
: 'border-gray-200'
|
||||||
|
}`}
|
||||||
placeholder="Cuéntanos sobre tu proyecto, equipo y qué quieres lograr con FlowSync..."
|
placeholder="Cuéntanos sobre tu proyecto, equipo y qué quieres lograr con FlowSync..."
|
||||||
rows={5}
|
rows={5}
|
||||||
value={formData.message}
|
value={formData.message}
|
||||||
@@ -350,15 +403,15 @@ export default function ContactForm() {
|
|||||||
aria-describedby={errors.message && touched.message ? 'message-error' : undefined}
|
aria-describedby={errors.message && touched.message ? 'message-error' : undefined}
|
||||||
aria-invalid={!!(errors.message && touched.message)}
|
aria-invalid={!!(errors.message && touched.message)}
|
||||||
/>
|
/>
|
||||||
<div className={styles.textareaFooter}>
|
<div className="flex justify-between items-center">
|
||||||
{errors.message && touched.message ? (
|
{errors.message && touched.message ? (
|
||||||
<span id="message-error" className={styles.errorMsg} role="alert">
|
<span id="message-error" className="text-xs text-error font-medium flex items-center gap-1" role="alert">
|
||||||
{errors.message}
|
{errors.message}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span />
|
<span />
|
||||||
)}
|
)}
|
||||||
<span className={styles.charCount}>
|
<span className="text-xs text-gray-400 ml-auto">
|
||||||
{formData.message.length} caracteres
|
{formData.message.length} caracteres
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -367,30 +420,41 @@ export default function ContactForm() {
|
|||||||
{/* Submit */}
|
{/* Submit */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className={`btn btn-primary btn-lg ${styles.submitBtn}`}
|
className="btn btn-primary btn-lg w-full justify-center mt-2 disabled:opacity-70 disabled:cursor-not-allowed disabled:transform-none"
|
||||||
disabled={status === 'loading'}
|
disabled={status === 'loading'}
|
||||||
id="contact-submit-btn"
|
id="contact-submit-btn"
|
||||||
aria-busy={status === 'loading'}
|
aria-busy={status === 'loading'}
|
||||||
>
|
>
|
||||||
{status === 'loading' ? (
|
{status === 'loading' ? (
|
||||||
<>
|
<>
|
||||||
<span className={styles.spinner} aria-hidden="true" />
|
<span
|
||||||
|
className="w-[18px] h-[18px] border-2 border-white/30 border-t-white rounded-full animate-[spin_0.7s_linear_infinite] shrink-0"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
Enviando...
|
Enviando...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
Enviar mensaje
|
Enviar mensaje
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||||
<path d="M2 8h12M10 4l4 4-4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
|
d="M2 8h12M10 4l4 4-4 4"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p className={styles.privacyNote}>
|
<p className="text-xs text-gray-400 text-center leading-relaxed">
|
||||||
Al enviar, aceptas nuestra{' '}
|
Al enviar, aceptas nuestra{' '}
|
||||||
<a href="#" className={styles.privacyLink}>política de privacidad</a>.
|
<a href="#" className="text-gray-600 underline underline-offset-2 transition-colors duration-150 hover:text-black">
|
||||||
Nunca compartiremos tu información.
|
política de privacidad
|
||||||
|
</a>
|
||||||
|
. Nunca compartiremos tu información.
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
.features {
|
|
||||||
background: var(--color-gray-50);
|
|
||||||
border-top: 1px solid var(--color-gray-200);
|
|
||||||
border-bottom: 1px solid var(--color-gray-200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reveal */
|
|
||||||
.reveal {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(24px);
|
|
||||||
transition: opacity 0.65s ease, transform 0.65s ease;
|
|
||||||
}
|
|
||||||
.reveal.visible {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header */
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto var(--space-16);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: clamp(var(--text-3xl), 5vw, var(--text-5xl));
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
line-height: 1.1;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titleAccent {
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grid */
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card */
|
|
||||||
.card {
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
padding: var(--space-8);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
transition: all var(--transition-base);
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
border-color: var(--color-black);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardIcon {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
transition: transform var(--transition-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover .cardIcon {
|
|
||||||
transform: scale(1.05) rotate(-2deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardContent {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardTag {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardTitle {
|
|
||||||
font-size: var(--text-xl);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.03em;
|
|
||||||
color: var(--color-black);
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardDesc {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
line-height: 1.65;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bottom CTA */
|
|
||||||
.bottomCta {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: var(--space-16);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaText {
|
|
||||||
font-size: var(--text-xl);
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--color-black);
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: var(--space-6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import styles from './Features.module.css';
|
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
|
d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
title: 'Automatización inteligente',
|
title: 'Automatización inteligente',
|
||||||
@@ -18,7 +23,13 @@ const features = [
|
|||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M9 11a4 4 0 100-8 4 4 0 000 8zM23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
|
d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M9 11a4 4 0 100-8 4 4 0 000 8zM23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
title: 'Colaboración en tiempo real',
|
title: 'Colaboración en tiempo real',
|
||||||
@@ -29,7 +40,13 @@ const features = [
|
|||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<polyline
|
||||||
|
points="22 12 18 12 15 21 9 3 6 12 2 12"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
title: 'Analíticas avanzadas',
|
title: 'Analíticas avanzadas',
|
||||||
@@ -53,7 +70,11 @@ const features = [
|
|||||||
icon: (
|
icon: (
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
|
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
|
||||||
<path d="M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z" stroke="currentColor" strokeWidth="2" />
|
<path
|
||||||
|
d="M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
title: '200+ integraciones',
|
title: '200+ integraciones',
|
||||||
@@ -64,7 +85,13 @@ const features = [
|
|||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
|
d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
title: 'Soporte 24/7',
|
title: 'Soporte 24/7',
|
||||||
@@ -82,57 +109,74 @@ export default function Features() {
|
|||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
entry.target.classList.add(styles.visible);
|
entry.target.classList.add('opacity-100', 'translate-y-0');
|
||||||
|
entry.target.classList.remove('opacity-0', 'translate-y-6');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ threshold: 0.1 }
|
{ threshold: 0.1 }
|
||||||
);
|
);
|
||||||
|
|
||||||
const elements = sectionRef.current?.querySelectorAll(`.${styles.reveal}`);
|
const elements = sectionRef.current?.querySelectorAll('.reveal');
|
||||||
elements?.forEach((el) => observer.observe(el));
|
elements?.forEach((el) => observer.observe(el));
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`section ${styles.features}`} id="features" ref={sectionRef} aria-labelledby="features-heading">
|
<section
|
||||||
|
className="bg-gray-50 border-y border-gray-200 py-24"
|
||||||
|
id="features"
|
||||||
|
ref={sectionRef}
|
||||||
|
aria-labelledby="features-heading"
|
||||||
|
>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${styles.reveal} ${styles.header}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out text-center max-w-[600px] mx-auto mb-16 flex flex-col items-center gap-4">
|
||||||
<span className="badge badge-accent">Características</span>
|
<span className="badge badge-accent">Características</span>
|
||||||
<h2 id="features-heading" className={styles.title}>
|
<h2
|
||||||
|
id="features-heading"
|
||||||
|
className="text-[clamp(1.875rem,5vw,3rem)] font-black tracking-[-0.04em] leading-[1.1] text-black"
|
||||||
|
>
|
||||||
Todo lo que necesitas,
|
Todo lo que necesitas,
|
||||||
<br />
|
<br />
|
||||||
<span className={styles.titleAccent}>nada que no necesitas</span>
|
<span className="text-gray-600">nada que no necesitas</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p className={styles.subtitle}>
|
<p className="text-lg text-gray-600 leading-relaxed">
|
||||||
Diseñado para equipos que quieren mover rápido sin romper cosas.
|
Diseñado para equipos que quieren mover rápido sin romper cosas.
|
||||||
Potente cuando lo necesitas, simple cuando no.
|
Potente cuando lo necesitas, simple cuando no.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Grid */}
|
{/* Grid */}
|
||||||
<div className={styles.grid}>
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{features.map((feature, i) => (
|
{features.map((feature, i) => (
|
||||||
<article
|
<article
|
||||||
key={feature.title}
|
key={feature.title}
|
||||||
className={`${styles.reveal} ${styles.card}`}
|
className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out bg-white border border-gray-200 rounded-xl p-8 max-sm:p-6 flex flex-col gap-4 cursor-default hover:border-black hover:shadow-lg hover:-translate-y-0.5 group"
|
||||||
style={{ transitionDelay: `${i * 0.08}s` }}
|
style={{ transitionDelay: `${i * 80}ms` }}
|
||||||
aria-label={feature.title}
|
aria-label={feature.title}
|
||||||
>
|
>
|
||||||
<div className={styles.cardIcon}>{feature.icon}</div>
|
<div className="w-12 h-12 bg-black text-white rounded-lg flex items-center justify-center shrink-0 transition-transform duration-250 ease-out group-hover:scale-105 group-hover:-rotate-2">
|
||||||
<div className={styles.cardContent}>
|
{feature.icon}
|
||||||
<div className={styles.cardTag}>{feature.tag}</div>
|
</div>
|
||||||
<h3 className={styles.cardTitle}>{feature.title}</h3>
|
<div className="flex flex-col gap-2">
|
||||||
<p className={styles.cardDesc}>{feature.description}</p>
|
<div className="text-xs font-bold uppercase tracking-widest text-gray-400">
|
||||||
|
{feature.tag}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-extrabold tracking-tight text-black leading-tight">
|
||||||
|
{feature.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 leading-relaxed">
|
||||||
|
{feature.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom CTA */}
|
{/* Bottom CTA */}
|
||||||
<div className={`${styles.reveal} ${styles.bottomCta}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out text-center mt-16 flex flex-col items-center gap-4">
|
||||||
<p className={styles.ctaText}>
|
<p className="text-xl font-bold text-black tracking-tight">
|
||||||
¿Listo para transformar cómo trabaja tu equipo?
|
¿Listo para transformar cómo trabaja tu equipo?
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
.footer {
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CTA Banner */
|
|
||||||
.ctaBanner {
|
|
||||||
background: var(--color-gray-900);
|
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaInner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--space-8);
|
|
||||||
padding: var(--space-12) 0;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaText {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaTitle {
|
|
||||||
font-size: clamp(var(--text-2xl), 4vw, var(--text-4xl));
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaSubtitle {
|
|
||||||
font-size: var(--text-base);
|
|
||||||
color: rgba(255,255,255,0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaBtns {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-3);
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main footer */
|
|
||||||
.main {
|
|
||||||
padding: var(--space-16) 0 var(--space-8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.topRow {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 280px 1fr;
|
|
||||||
gap: var(--space-16);
|
|
||||||
margin-bottom: var(--space-12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Brand */
|
|
||||||
.brand {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.brandLogo {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoMark {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
background: var(--color-white);
|
|
||||||
color: var(--color-black);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoText {
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.brandDesc {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: rgba(255,255,255,0.45);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Socials */
|
|
||||||
.socials {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.socialLink {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
background: rgba(255,255,255,0.08);
|
|
||||||
color: rgba(255,255,255,0.6);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.socialLink:hover {
|
|
||||||
background: rgba(255,255,255,0.15);
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Links grid */
|
|
||||||
.linksGrid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
gap: var(--space-8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkColumn {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkCategory {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
color: rgba(255,255,255,0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkList {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footerLink {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: rgba(255,255,255,0.55);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footerLink:hover {
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bottom bar */
|
|
||||||
.bottomBar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-top: var(--space-8);
|
|
||||||
border-top: 1px solid rgba(255,255,255,0.08);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.copyright {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: rgba(255,255,255,0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusBadge {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 500;
|
|
||||||
color: rgba(255,255,255,0.45);
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusDot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
background: var(--color-success);
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: pulse 2s ease infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(0, 200, 83, 0.4); }
|
|
||||||
50% { opacity: 0.8; box-shadow: 0 0 0 4px rgba(0, 200, 83, 0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.topRow {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: var(--space-12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.linksGrid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.ctaInner {
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaBtns {
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctaBtns .btn {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.linksGrid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: var(--space-6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import styles from './Footer.module.css';
|
|
||||||
|
|
||||||
const footerLinks = {
|
const footerLinks = {
|
||||||
Producto: ['Características', 'Precios', 'Integraciones', 'Changelog', 'Roadmap'],
|
Producto: ['Características', 'Precios', 'Integraciones', 'Changelog', 'Roadmap'],
|
||||||
Empresa: ['Sobre nosotros', 'Blog', 'Carreras', 'Prensa', 'Partners'],
|
Empresa: ['Sobre nosotros', 'Blog', 'Carreras', 'Prensa', 'Partners'],
|
||||||
@@ -41,31 +39,30 @@ const socials = [
|
|||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className={styles.footer} role="contentinfo">
|
<footer className="bg-black text-white" role="contentinfo">
|
||||||
{/* CTA Banner */}
|
{/* CTA Banner */}
|
||||||
<div className={styles.ctaBanner}>
|
<div className="bg-gray-900 border-b border-white/10">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className={styles.ctaInner}>
|
<div className="flex items-center justify-between gap-8 py-12 flex-wrap max-sm:flex-col max-sm:text-center">
|
||||||
<div className={styles.ctaText}>
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className={styles.ctaTitle}>
|
<h2 className="text-[clamp(1.5rem,4vw,2.25rem)] font-black tracking-[-0.04em] text-white">
|
||||||
Empieza hoy mismo — es gratis
|
Empieza hoy mismo — es gratis
|
||||||
</h2>
|
</h2>
|
||||||
<p className={styles.ctaSubtitle}>
|
<p className="text-base text-white/50">
|
||||||
Únete a más de 10,000 equipos que ya usan FlowSync
|
Únete a más de 10,000 equipos que ya usan FlowSync
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.ctaBtns}>
|
<div className="flex gap-3 shrink-0 flex-wrap max-sm:w-full max-sm:flex-col">
|
||||||
<button
|
<button
|
||||||
className="btn btn-accent btn-lg"
|
className="btn btn-accent btn-lg max-sm:w-full max-sm:justify-center"
|
||||||
id="footer-cta-btn"
|
id="footer-cta-btn"
|
||||||
onClick={() => document.querySelector('#contact')?.scrollIntoView({ behavior: 'smooth' })}
|
onClick={() => document.querySelector('#contact')?.scrollIntoView({ behavior: 'smooth' })}
|
||||||
>
|
>
|
||||||
Prueba gratis 14 días
|
Prueba gratis 14 días
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary btn-lg"
|
className="btn btn-secondary btn-lg border-white/20 text-white bg-white/10 hover:bg-white/20 hover:border-white/30 max-sm:w-full max-sm:justify-center"
|
||||||
id="footer-demo-btn"
|
id="footer-demo-btn"
|
||||||
style={{ background: 'rgba(255,255,255,0.1)', borderColor: 'rgba(255,255,255,0.2)', color: 'white' }}
|
|
||||||
>
|
>
|
||||||
Ver demo
|
Ver demo
|
||||||
</button>
|
</button>
|
||||||
@@ -75,24 +72,28 @@ export default function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main footer */}
|
{/* Main footer */}
|
||||||
<div className={styles.main}>
|
<div className="py-16 pb-8">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className={styles.topRow}>
|
<div className="grid grid-cols-[280px_1fr] max-lg:grid-cols-1 gap-16 lg:gap-16 max-lg:gap-12 mb-12">
|
||||||
{/* Brand */}
|
{/* Brand */}
|
||||||
<div className={styles.brand}>
|
<div className="flex flex-col gap-4">
|
||||||
<div className={styles.brandLogo}>
|
<div className="flex items-center gap-2">
|
||||||
<span className={styles.logoMark}>FS</span>
|
<span className="w-9 h-9 bg-white text-black rounded-md flex items-center justify-center text-xs font-extrabold tracking-[-0.02em]">
|
||||||
<span className={styles.logoText}>FlowSync</span>
|
FS
|
||||||
|
</span>
|
||||||
|
<span className="text-lg font-extrabold tracking-[-0.04em] text-white">
|
||||||
|
FlowSync
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.brandDesc}>
|
<p className="text-sm text-white/45 leading-relaxed">
|
||||||
La plataforma todo-en-uno para equipos que quieren moverse rápido sin perder el control.
|
La plataforma todo-en-uno para equipos que quieren moverse rápido sin perder el control.
|
||||||
</p>
|
</p>
|
||||||
<div className={styles.socials}>
|
<div className="flex gap-2">
|
||||||
{socials.map((s) => (
|
{socials.map((s) => (
|
||||||
<a
|
<a
|
||||||
key={s.name}
|
key={s.name}
|
||||||
href={s.href}
|
href={s.href}
|
||||||
className={styles.socialLink}
|
className="w-9 h-9 bg-white/10 text-white/60 rounded-md flex items-center justify-center transition-all duration-150 hover:bg-white/15 hover:text-white"
|
||||||
aria-label={s.name}
|
aria-label={s.name}
|
||||||
id={`footer-social-${s.name.toLowerCase()}`}
|
id={`footer-social-${s.name.toLowerCase()}`}
|
||||||
>
|
>
|
||||||
@@ -103,14 +104,19 @@ export default function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Links */}
|
{/* Links */}
|
||||||
<nav className={styles.linksGrid} aria-label="Footer navigation">
|
<nav className="grid grid-cols-4 max-lg:grid-cols-2 max-sm:grid-cols-2 max-sm:gap-6 gap-8" aria-label="Footer navigation">
|
||||||
{Object.entries(footerLinks).map(([category, links]) => (
|
{Object.entries(footerLinks).map(([category, links]) => (
|
||||||
<div key={category} className={styles.linkColumn}>
|
<div key={category} className="flex flex-col gap-4">
|
||||||
<h3 className={styles.linkCategory}>{category}</h3>
|
<h3 className="text-xs font-bold uppercase tracking-widest text-white/35">
|
||||||
<ul className={styles.linkList} role="list">
|
{category}
|
||||||
|
</h3>
|
||||||
|
<ul className="list-none flex flex-col gap-3" role="list">
|
||||||
{links.map((link) => (
|
{links.map((link) => (
|
||||||
<li key={link}>
|
<li key={link}>
|
||||||
<a href="#" className={styles.footerLink}>
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-sm text-white/55 no-underline transition-colors duration-150 hover:text-white"
|
||||||
|
>
|
||||||
{link}
|
{link}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -122,12 +128,12 @@ export default function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom bar */}
|
{/* Bottom bar */}
|
||||||
<div className={styles.bottomBar}>
|
<div className="flex items-center justify-between pt-8 border-t border-white/10 flex-wrap gap-4">
|
||||||
<p className={styles.copyright}>
|
<p className="text-sm text-white/35">
|
||||||
© {new Date().getFullYear()} FlowSync, Inc. Todos los derechos reservados.
|
© {new Date().getFullYear()} FlowSync, Inc. Todos los derechos reservados.
|
||||||
</p>
|
</p>
|
||||||
<div className={styles.statusBadge}>
|
<div className="flex items-center gap-2 text-xs font-medium text-white/45">
|
||||||
<span className={styles.statusDot} />
|
<span className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
|
||||||
Todos los sistemas operativos
|
Todos los sistemas operativos
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,370 +0,0 @@
|
|||||||
.hero {
|
|
||||||
padding-top: 72px; /* navbar height */
|
|
||||||
background: var(--color-white);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: var(--space-20);
|
|
||||||
padding-bottom: var(--space-16);
|
|
||||||
gap: var(--space-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reveal animation */
|
|
||||||
.reveal {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
transition: opacity 0.7s ease, transform 0.7s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reveal.visible {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reveal:nth-child(2) { transition-delay: 0.1s; }
|
|
||||||
.reveal:nth-child(3) { transition-delay: 0.2s; }
|
|
||||||
.reveal:nth-child(4) { transition-delay: 0.3s; }
|
|
||||||
.reveal:nth-child(5) { transition-delay: 0.4s; }
|
|
||||||
.reveal:nth-child(6) { transition-delay: 0.5s; }
|
|
||||||
.reveal:nth-child(7) { transition-delay: 0.6s; }
|
|
||||||
.reveal:nth-child(8) { transition-delay: 0.7s; }
|
|
||||||
|
|
||||||
/* Badge */
|
|
||||||
.badgeWrap {
|
|
||||||
margin-bottom: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Heading */
|
|
||||||
.heading {
|
|
||||||
font-size: clamp(2.5rem, 7vw, 5rem);
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
line-height: 1.05;
|
|
||||||
color: var(--color-black);
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.emphasis {
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 900;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subheading */
|
|
||||||
.subheading {
|
|
||||||
font-size: clamp(var(--text-base), 2vw, var(--text-xl));
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
max-width: 520px;
|
|
||||||
line-height: 1.6;
|
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CTAs */
|
|
||||||
.ctas {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-3);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Trust note */
|
|
||||||
.trustNote {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
margin-top: var(--space-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dashboard Mockup */
|
|
||||||
.mockupWrap {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 900px;
|
|
||||||
margin-top: var(--space-8);
|
|
||||||
perspective: 1200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockup {
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
box-shadow: var(--shadow-xl), 0 0 0 1px rgba(0, 0, 0, 0.03);
|
|
||||||
overflow: hidden;
|
|
||||||
transform: rotateX(4deg);
|
|
||||||
transition: transform 0.4s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockup:hover {
|
|
||||||
transform: rotateX(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Window bar */
|
|
||||||
.mockupBar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
padding: var(--space-3) var(--space-4);
|
|
||||||
background: var(--color-gray-50);
|
|
||||||
border-bottom: 1px solid var(--color-gray-200);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
border-radius: 50%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockupUrl {
|
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-full);
|
|
||||||
padding: 2px var(--space-3);
|
|
||||||
max-width: 280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mockup body */
|
|
||||||
.mockupBody {
|
|
||||||
display: flex;
|
|
||||||
height: 380px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
.mockupSidebar {
|
|
||||||
width: 56px;
|
|
||||||
background: var(--color-gray-50);
|
|
||||||
border-right: 1px solid var(--color-gray-200);
|
|
||||||
padding: var(--space-4) var(--space-3);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-3);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarLogo {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
background: var(--color-black);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
margin-bottom: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarItem {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
background: var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarActive {
|
|
||||||
background: var(--color-black) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main dashboard */
|
|
||||||
.mockupMain {
|
|
||||||
flex: 1;
|
|
||||||
padding: var(--space-6);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-4);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockupHeader {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockupTitle {
|
|
||||||
height: 20px;
|
|
||||||
width: 160px;
|
|
||||||
background: var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockupBtn {
|
|
||||||
height: 32px;
|
|
||||||
width: 100px;
|
|
||||||
background: var(--color-black);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Metric cards */
|
|
||||||
.metricsRow {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.metricCard {
|
|
||||||
background: var(--color-gray-50);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
padding: var(--space-3);
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-2);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metricIcon {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metricLines {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metricValue {
|
|
||||||
height: 14px;
|
|
||||||
background: var(--color-gray-300);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metricLabel {
|
|
||||||
height: 10px;
|
|
||||||
background: var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Chart */
|
|
||||||
.chartArea {
|
|
||||||
flex: 1;
|
|
||||||
background: var(--color-gray-50);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
padding: var(--space-4);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chartBars {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: var(--space-2);
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes growBar {
|
|
||||||
from { height: 0; }
|
|
||||||
to { height: var(--target-height); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar {
|
|
||||||
flex: 1;
|
|
||||||
background: var(--color-black);
|
|
||||||
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
|
||||||
opacity: 0.12;
|
|
||||||
animation: fadeIn 0.5s ease forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar:nth-child(odd) {
|
|
||||||
opacity: 0.08;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar:nth-child(4),
|
|
||||||
.bar:nth-child(8) {
|
|
||||||
opacity: 0.9;
|
|
||||||
background: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Task list */
|
|
||||||
.taskList {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.taskRow {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.taskCheck {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border: 2px solid var(--color-gray-300);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.taskLine {
|
|
||||||
height: 10px;
|
|
||||||
background: var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stats */
|
|
||||||
.stats {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-12);
|
|
||||||
margin-top: var(--space-8);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statItem {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.statValue {
|
|
||||||
font-size: var(--text-3xl);
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.statLabel {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-500, #666);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.mockupBody {
|
|
||||||
height: 260px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mockupSidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metricsRow {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats {
|
|
||||||
gap: var(--space-8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.ctas {
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctas .btn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import styles from './Hero.module.css';
|
|
||||||
|
|
||||||
const stats = [
|
const stats = [
|
||||||
{ value: '10K+', label: 'Equipos activos' },
|
{ value: '10K+', label: 'Equipos activos' },
|
||||||
@@ -18,14 +17,15 @@ export default function Hero() {
|
|||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
entry.target.classList.add(styles.visible);
|
entry.target.classList.add('opacity-100', 'translate-y-0');
|
||||||
|
entry.target.classList.remove('opacity-0', 'translate-y-6');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ threshold: 0.1 }
|
{ threshold: 0.1 }
|
||||||
);
|
);
|
||||||
|
|
||||||
const elements = heroRef.current?.querySelectorAll(`.${styles.reveal}`);
|
const elements = heroRef.current?.querySelectorAll('.reveal');
|
||||||
elements?.forEach((el) => observer.observe(el));
|
elements?.forEach((el) => observer.observe(el));
|
||||||
|
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
@@ -40,10 +40,15 @@ export default function Hero() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.hero} id="hero" ref={heroRef} aria-label="Sección principal">
|
<section
|
||||||
<div className={`container ${styles.inner}`}>
|
className="pt-[72px] bg-white overflow-hidden"
|
||||||
|
id="hero"
|
||||||
|
ref={heroRef}
|
||||||
|
aria-label="Sección principal"
|
||||||
|
>
|
||||||
|
<div className="container flex flex-col items-center text-center pt-20 pb-16 gap-6">
|
||||||
{/* Badge */}
|
{/* Badge */}
|
||||||
<div className={`${styles.reveal} ${styles.badgeWrap}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-100 mb-2">
|
||||||
<span className="badge badge-dark">
|
<span className="badge badge-dark">
|
||||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" aria-hidden="true">
|
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" aria-hidden="true">
|
||||||
<circle cx="4" cy="4" r="4" fill="#00c853" />
|
<circle cx="4" cy="4" r="4" fill="#00c853" />
|
||||||
@@ -53,34 +58,40 @@ export default function Hero() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Heading */}
|
{/* Heading */}
|
||||||
<h1 className={`${styles.reveal} ${styles.heading}`}>
|
<h1 className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-200 text-[clamp(2.5rem,7vw,5rem)] font-black tracking-[-0.04em] leading-[1.05] text-black max-w-[800px]">
|
||||||
El flujo de trabajo
|
El flujo de trabajo
|
||||||
<br />
|
<br />
|
||||||
<em className={styles.emphasis}>que tu equipo</em>
|
<em className="italic font-black">que tu equipo</em>
|
||||||
<br />
|
<br />
|
||||||
siempre necesitó
|
siempre necesitó
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{/* Subheading */}
|
{/* Subheading */}
|
||||||
<p className={`${styles.reveal} ${styles.subheading}`}>
|
<p className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-300 text-[clamp(1rem,2vw,1.25rem)] text-gray-600 max-w-[520px] leading-relaxed mt-2">
|
||||||
FlowSync conecta a tu equipo, automatiza tareas repetitivas y te da
|
FlowSync conecta a tu equipo, automatiza tareas repetitivas y te da
|
||||||
visibilidad total de cada proyecto — todo en un solo lugar.
|
visibilidad total de cada proyecto — todo en un solo lugar.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* CTA Buttons */}
|
{/* CTA Buttons */}
|
||||||
<div className={`${styles.reveal} ${styles.ctas}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-400 flex flex-wrap justify-center gap-3 mt-2 max-sm:flex-col max-sm:w-full max-sm:max-w-[320px]">
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-lg"
|
className="btn btn-primary btn-lg max-sm:w-full"
|
||||||
id="hero-cta-primary"
|
id="hero-cta-primary"
|
||||||
onClick={handleScrollToContact}
|
onClick={handleScrollToContact}
|
||||||
>
|
>
|
||||||
Empieza gratis
|
Empieza gratis
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||||
<path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
|
d="M3 8h10M9 4l4 4-4 4"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary btn-lg"
|
className="btn btn-secondary btn-lg max-sm:w-full"
|
||||||
id="hero-cta-secondary"
|
id="hero-cta-secondary"
|
||||||
onClick={handleScrollToFeatures}
|
onClick={handleScrollToFeatures}
|
||||||
>
|
>
|
||||||
@@ -89,71 +100,92 @@ export default function Hero() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Trust note */}
|
{/* Trust note */}
|
||||||
<p className={`${styles.reveal} ${styles.trustNote}`}>
|
<p className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-500 text-sm text-gray-400 mt-1">
|
||||||
Sin tarjeta de crédito · 14 días gratis · Cancela cuando quieras
|
Sin tarjeta de crédito · 14 días gratis · Cancela cuando quieras
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Dashboard mockup */}
|
{/* Dashboard mockup */}
|
||||||
<div className={`${styles.reveal} ${styles.mockupWrap}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-600 w-full max-w-[900px] mt-8 [perspective:1200px]">
|
||||||
<div className={styles.mockup} role="img" aria-label="Vista previa del dashboard de FlowSync">
|
<div
|
||||||
|
className="bg-white border border-gray-200 rounded-2xl shadow-xl overflow-hidden transform rotate-x-4 transition-transform duration-400 ease-out hover:rotate-x-0"
|
||||||
|
role="img"
|
||||||
|
aria-label="Vista previa del dashboard de FlowSync"
|
||||||
|
style={{ transform: 'rotateX(4deg)' }}
|
||||||
|
>
|
||||||
{/* Window chrome */}
|
{/* Window chrome */}
|
||||||
<div className={styles.mockupBar}>
|
<div className="flex items-center gap-2 px-4 py-3 bg-gray-50 border-b border-gray-200">
|
||||||
<span className={styles.dot} style={{ background: '#ff5f57' }} />
|
<span className="w-3 h-3 rounded-full shrink-0 bg-[#ff5f57]" />
|
||||||
<span className={styles.dot} style={{ background: '#febc2e' }} />
|
<span className="w-3 h-3 rounded-full shrink-0 bg-[#febc2e]" />
|
||||||
<span className={styles.dot} style={{ background: '#28c840' }} />
|
<span className="w-3 h-3 rounded-full shrink-0 bg-[#28c840]" />
|
||||||
<span className={styles.mockupUrl}>app.flowsync.io/dashboard</span>
|
<span className="flex-1 text-center text-xs text-gray-400 bg-white border border-gray-200 rounded-full py-[2px] px-3 max-w-[280px] mx-auto">
|
||||||
|
app.flowsync.io/dashboard
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mock dashboard content */}
|
{/* Mock dashboard content */}
|
||||||
<div className={styles.mockupBody}>
|
<div className="flex h-[380px] max-md:h-[260px]">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<nav className={styles.mockupSidebar} aria-hidden="true">
|
<nav className="w-14 bg-gray-50 border-r border-gray-200 px-3 py-4 flex flex-col gap-3 shrink-0 max-md:hidden" aria-hidden="true">
|
||||||
<div className={styles.sidebarLogo} />
|
<div className="w-8 h-8 bg-black rounded-md mb-4" />
|
||||||
{[...Array(5)].map((_, i) => (
|
{[...Array(5)].map((_, i) => (
|
||||||
<div key={i} className={`${styles.sidebarItem} ${i === 0 ? styles.sidebarActive : ''}`} />
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`w-8 h-8 rounded-md ${
|
||||||
|
i === 0 ? 'bg-black' : 'bg-gray-200'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<main className={styles.mockupMain} aria-hidden="true">
|
<main className="flex-1 p-6 flex flex-col gap-4 overflow-hidden" aria-hidden="true">
|
||||||
{/* Header row */}
|
{/* Header row */}
|
||||||
<div className={styles.mockupHeader}>
|
<div className="flex justify-between items-center">
|
||||||
<div className={styles.mockupTitle} />
|
<div className="h-5 w-40 bg-gray-200 rounded-sm" />
|
||||||
<div className={styles.mockupBtn} />
|
<div className="h-8 w-24 bg-black rounded-md" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Metric cards */}
|
{/* Metric cards */}
|
||||||
<div className={styles.metricsRow}>
|
<div className="grid grid-cols-4 max-md:grid-cols-2 gap-3">
|
||||||
{['#0066ff', '#00c853', '#ff9500', '#6c5ce7'].map((color, i) => (
|
{['#0066ff', '#00c853', '#ff9500', '#6c5ce7'].map((color, i) => (
|
||||||
<div key={i} className={styles.metricCard}>
|
<div key={i} className="bg-gray-50 border border-gray-200 rounded-xl p-3 flex gap-2 items-center">
|
||||||
<div className={styles.metricIcon} style={{ background: `${color}18`, color }} />
|
<div
|
||||||
<div className={styles.metricLines}>
|
className="w-8 h-8 rounded-md shrink-0"
|
||||||
<div className={styles.metricValue} />
|
style={{ background: `${color}18`, color }}
|
||||||
<div className={styles.metricLabel} />
|
/>
|
||||||
|
<div className="flex flex-col gap-[6px] flex-1">
|
||||||
|
<div className="h-[14px] bg-gray-300 rounded-sm w-[70%]" />
|
||||||
|
<div className="h-[10px] bg-gray-200 rounded-sm w-[90%]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chart area */}
|
{/* Chart area */}
|
||||||
<div className={styles.chartArea}>
|
<div className="flex-1 bg-gray-50 border border-gray-200 rounded-xl p-4 overflow-hidden">
|
||||||
<div className={styles.chartBars}>
|
<div className="flex items-end gap-2 h-100">
|
||||||
{[60, 80, 50, 90, 70, 85, 65, 95, 75, 88].map((h, i) => (
|
{[60, 80, 50, 90, 70, 85, 65, 95, 75, 88].map((h, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className={styles.bar}
|
className={`flex-1 rounded-t-sm animate-fadeIn opacity-100 ${
|
||||||
style={{ height: `${h}%`, animationDelay: `${i * 0.05}s` }}
|
i === 3 || i === 7 ? 'bg-black opacity-90' : 'bg-black opacity-10'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
height: `${h}%`,
|
||||||
|
animationDelay: `${i * 0.05}s`,
|
||||||
|
opacity: i === 3 || i === 7 ? 0.9 : (i % 2 === 0 ? 0.12 : 0.08)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Task list */}
|
{/* Task list */}
|
||||||
<div className={styles.taskList}>
|
<div className="flex flex-col gap-2">
|
||||||
{[85, 60, 95, 70].map((w, i) => (
|
{[85, 60, 95, 70].map((w, i) => (
|
||||||
<div key={i} className={styles.taskRow}>
|
<div key={i} className="flex items-center gap-3">
|
||||||
<div className={styles.taskCheck} />
|
<div className="w-4 h-4 border-2 border-gray-300 rounded-sm shrink-0" />
|
||||||
<div className={styles.taskLine} style={{ width: `${w}%` }} />
|
<div className="h-[10px] bg-gray-200 rounded-sm" style={{ width: `${w}%` }} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -163,11 +195,15 @@ export default function Hero() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className={`${styles.reveal} ${styles.stats}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out delay-700 flex flex-wrap justify-center gap-12 max-md:gap-8 mt-8">
|
||||||
{stats.map((stat) => (
|
{stats.map((stat) => (
|
||||||
<div key={stat.label} className={styles.statItem}>
|
<div key={stat.label} className="flex flex-col items-center gap-1">
|
||||||
<span className={styles.statValue}>{stat.value}</span>
|
<span className="text-3xl font-black tracking-[-0.04em] text-black">
|
||||||
<span className={styles.statLabel}>{stat.label}</span>
|
{stat.value}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
{stat.label}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
.navbar {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
-webkit-backdrop-filter: blur(12px);
|
|
||||||
border-bottom: 1px solid transparent;
|
|
||||||
transition: all var(--transition-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrolled {
|
|
||||||
border-bottom-color: var(--color-gray-200);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--space-8);
|
|
||||||
height: 72px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Logo */
|
|
||||||
.logo {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
text-decoration: none;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoMark {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
background: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoText {
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
font-weight: 800;
|
|
||||||
color: var(--color-black);
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Desktop links */
|
|
||||||
.links {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-1);
|
|
||||||
list-style: none;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
padding: var(--space-2) var(--space-3);
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
color: var(--color-black);
|
|
||||||
background: var(--color-gray-100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Desktop actions */
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile toggle */
|
|
||||||
.menuToggle {
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
padding: var(--space-2);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar {
|
|
||||||
display: block;
|
|
||||||
width: 22px;
|
|
||||||
height: 2px;
|
|
||||||
background: var(--color-black);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar.open:nth-child(1) {
|
|
||||||
transform: translateY(7px) rotate(45deg);
|
|
||||||
}
|
|
||||||
.bar.open:nth-child(2) {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scaleX(0);
|
|
||||||
}
|
|
||||||
.bar.open:nth-child(3) {
|
|
||||||
transform: translateY(-7px) rotate(-45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile menu */
|
|
||||||
.mobileMenu {
|
|
||||||
background: var(--color-white);
|
|
||||||
border-top: 1px solid var(--color-gray-200);
|
|
||||||
padding: var(--space-4) var(--space-6);
|
|
||||||
animation: fadeInUp 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobileLinks {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-1);
|
|
||||||
margin-bottom: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobileLink {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
padding: var(--space-3) var(--space-4);
|
|
||||||
font-size: var(--text-base);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--color-dark);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
transition: background var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobileLink:hover {
|
|
||||||
background: var(--color-gray-100);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobileActions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.links,
|
|
||||||
.actions {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuToggle {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import styles from './Navbar.module.css';
|
|
||||||
|
|
||||||
const navLinks = [
|
const navLinks = [
|
||||||
{ label: 'Características', href: '#features' },
|
{ label: 'Características', href: '#features' },
|
||||||
@@ -26,20 +25,39 @@ export default function Navbar() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={`${styles.navbar} ${scrolled ? styles.scrolled : ''}`} role="banner">
|
<header
|
||||||
<nav className={`${styles.inner} container`} aria-label="Navegación principal">
|
className={`fixed top-0 left-0 right-0 z-[1000] bg-white/90 backdrop-blur-md border-b transition-all duration-250 ease-out ${
|
||||||
|
scrolled ? 'border-gray-200 shadow-sm' : 'border-transparent'
|
||||||
|
}`}
|
||||||
|
role="banner"
|
||||||
|
>
|
||||||
|
<nav
|
||||||
|
className="container flex items-center justify-between h-[72px] gap-8"
|
||||||
|
aria-label="Navegación principal"
|
||||||
|
>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<a href="#" className={styles.logo} aria-label="FlowSync - inicio">
|
<a
|
||||||
<span className={styles.logoMark}>FS</span>
|
href="#"
|
||||||
<span className={styles.logoText}>FlowSync</span>
|
className="flex items-center gap-2 no-underline shrink-0"
|
||||||
|
aria-label="FlowSync - inicio"
|
||||||
|
>
|
||||||
|
<span className="w-9 h-9 bg-black text-white rounded-md flex items-center justify-center text-xs font-extrabold tracking-[-0.02em]">
|
||||||
|
FS
|
||||||
|
</span>
|
||||||
|
<span className="text-lg font-extrabold text-black tracking-[-0.04em]">
|
||||||
|
FlowSync
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{/* Desktop links */}
|
{/* Desktop links */}
|
||||||
<ul className={styles.links} role="list">
|
<ul
|
||||||
|
className="hidden md:flex items-center justify-center gap-1 list-none flex-1"
|
||||||
|
role="list"
|
||||||
|
>
|
||||||
{navLinks.map((link) => (
|
{navLinks.map((link) => (
|
||||||
<li key={link.href}>
|
<li key={link.href}>
|
||||||
<button
|
<button
|
||||||
className={styles.link}
|
className="px-3 py-2 text-sm font-medium text-gray-600 rounded-md transition-colors duration-150 ease-out bg-transparent border-none cursor-pointer hover:text-black hover:bg-gray-100"
|
||||||
onClick={() => handleNavClick(link.href)}
|
onClick={() => handleNavClick(link.href)}
|
||||||
aria-label={`Ir a sección ${link.label}`}
|
aria-label={`Ir a sección ${link.label}`}
|
||||||
>
|
>
|
||||||
@@ -50,7 +68,7 @@ export default function Navbar() {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{/* Desktop CTA */}
|
{/* Desktop CTA */}
|
||||||
<div className={styles.actions}>
|
<div className="hidden md:flex items-center gap-3 shrink-0">
|
||||||
<button className="btn btn-secondary" id="nav-login-btn">
|
<button className="btn btn-secondary" id="nav-login-btn">
|
||||||
Iniciar sesión
|
Iniciar sesión
|
||||||
</button>
|
</button>
|
||||||
@@ -65,26 +83,42 @@ export default function Navbar() {
|
|||||||
|
|
||||||
{/* Mobile toggle */}
|
{/* Mobile toggle */}
|
||||||
<button
|
<button
|
||||||
className={styles.menuToggle}
|
className="md:hidden flex flex-col gap-[5px] p-2 rounded-md bg-transparent border-none cursor-pointer"
|
||||||
onClick={() => setMenuOpen(!menuOpen)}
|
onClick={() => setMenuOpen(!menuOpen)}
|
||||||
aria-label={menuOpen ? 'Cerrar menú' : 'Abrir menú'}
|
aria-label={menuOpen ? 'Cerrar menú' : 'Abrir menú'}
|
||||||
aria-expanded={menuOpen}
|
aria-expanded={menuOpen}
|
||||||
id="nav-menu-toggle"
|
id="nav-menu-toggle"
|
||||||
>
|
>
|
||||||
<span className={`${styles.bar} ${menuOpen ? styles.open : ''}`} />
|
<span
|
||||||
<span className={`${styles.bar} ${menuOpen ? styles.open : ''}`} />
|
className={`block w-[22px] h-[2px] bg-black rounded-sm transition-transform duration-150 ease-out origin-center ${
|
||||||
<span className={`${styles.bar} ${menuOpen ? styles.open : ''}`} />
|
menuOpen ? 'translate-y-[7px] rotate-45' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={`block w-[22px] h-[2px] bg-black rounded-sm transition-all duration-150 ease-out ${
|
||||||
|
menuOpen ? 'opacity-0 scale-x-0' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={`block w-[22px] h-[2px] bg-black rounded-sm transition-transform duration-150 ease-out origin-center ${
|
||||||
|
menuOpen ? '-translate-y-[7px] -rotate-45' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Mobile menu */}
|
{/* Mobile menu */}
|
||||||
{menuOpen && (
|
{menuOpen && (
|
||||||
<div className={styles.mobileMenu} role="dialog" aria-label="Menú móvil">
|
<div
|
||||||
<ul className={styles.mobileLinks} role="list">
|
className="md:hidden bg-white border-t border-gray-200 px-6 py-4 animate-fadeInUp"
|
||||||
|
role="dialog"
|
||||||
|
aria-label="Menú móvil"
|
||||||
|
>
|
||||||
|
<ul className="list-none flex flex-col gap-1 mb-4" role="list">
|
||||||
{navLinks.map((link) => (
|
{navLinks.map((link) => (
|
||||||
<li key={link.href}>
|
<li key={link.href}>
|
||||||
<button
|
<button
|
||||||
className={styles.mobileLink}
|
className="block w-full text-left px-4 py-3 text-base font-medium text-dark rounded-md bg-transparent border-none cursor-pointer transition-colors duration-150 ease-out hover:bg-gray-100"
|
||||||
onClick={() => handleNavClick(link.href)}
|
onClick={() => handleNavClick(link.href)}
|
||||||
>
|
>
|
||||||
{link.label}
|
{link.label}
|
||||||
@@ -92,13 +126,10 @@ export default function Navbar() {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<div className={styles.mobileActions}>
|
<div className="flex flex-col gap-3">
|
||||||
<button className="btn btn-secondary" style={{ width: '100%' }}>
|
<button className="btn btn-secondary w-full">Iniciar sesión</button>
|
||||||
Iniciar sesión
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary w-full"
|
||||||
style={{ width: '100%' }}
|
|
||||||
onClick={() => handleNavClick('#contact')}
|
onClick={() => handleNavClick('#contact')}
|
||||||
>
|
>
|
||||||
Prueba gratis
|
Prueba gratis
|
||||||
|
|||||||
@@ -1,278 +0,0 @@
|
|||||||
.reveal {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(24px);
|
|
||||||
transition: opacity 0.65s ease, transform 0.65s ease;
|
|
||||||
}
|
|
||||||
.reveal.visible {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header */
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-4);
|
|
||||||
margin-bottom: var(--space-16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: clamp(var(--text-3xl), 5vw, var(--text-5xl));
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.04em;
|
|
||||||
line-height: 1.1;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: var(--text-lg);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toggle */
|
|
||||||
.toggle {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleLabel {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
transition: color var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleLabelActive {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleSwitch {
|
|
||||||
width: 48px;
|
|
||||||
height: 26px;
|
|
||||||
background: var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-full);
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
transition: background var(--transition-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleSwitch[aria-pressed='true'] {
|
|
||||||
background: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleKnob {
|
|
||||||
position: absolute;
|
|
||||||
top: 3px;
|
|
||||||
left: 3px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background: var(--color-white);
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: transform var(--transition-base);
|
|
||||||
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggleKnobOn {
|
|
||||||
transform: translateX(22px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.saveBadge {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: var(--space-2);
|
|
||||||
padding: 2px var(--space-2);
|
|
||||||
background: #dcfce7;
|
|
||||||
color: #15803d;
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 700;
|
|
||||||
border-radius: var(--radius-full);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Plans grid */
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: var(--space-6);
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card */
|
|
||||||
.card {
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-2xl);
|
|
||||||
padding: var(--space-8);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-6);
|
|
||||||
position: relative;
|
|
||||||
transition: all var(--transition-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight {
|
|
||||||
background: var(--color-black);
|
|
||||||
border-color: var(--color-black);
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight:hover {
|
|
||||||
box-shadow: 0 24px 64px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Popular badge */
|
|
||||||
.popularBadge {
|
|
||||||
position: absolute;
|
|
||||||
top: -14px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
background: var(--color-accent);
|
|
||||||
color: var(--color-white);
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: 700;
|
|
||||||
padding: var(--space-1) var(--space-4);
|
|
||||||
border-radius: var(--radius-full);
|
|
||||||
white-space: nowrap;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Plan header */
|
|
||||||
.planHeader {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.planName {
|
|
||||||
font-size: var(--text-2xl);
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.03em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight .planName {
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.planDesc {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight .planDesc {
|
|
||||||
color: rgba(255,255,255,0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Price */
|
|
||||||
.priceRow {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.priceCurrency {
|
|
||||||
font-size: var(--text-xl);
|
|
||||||
font-weight: 700;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.priceValue {
|
|
||||||
font-size: clamp(var(--text-4xl), 6vw, 3.5rem);
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: -0.05em;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.priceUnit {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
padding-bottom: 4px;
|
|
||||||
margin-left: var(--space-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight .priceUnit {
|
|
||||||
color: rgba(255,255,255,0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annualNote {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--color-gray-400);
|
|
||||||
margin-top: calc(var(--space-6) * -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight .annualNote {
|
|
||||||
color: rgba(255,255,255,0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Feature list */
|
|
||||||
.featureList {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.featureItem {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--color-gray-700, #444);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight .featureItem {
|
|
||||||
color: rgba(255,255,255,0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkIcon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardHighlight .checkIcon {
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enterprise note */
|
|
||||||
.enterpriseNote {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: var(--space-12);
|
|
||||||
font-size: var(--text-base);
|
|
||||||
color: var(--color-gray-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
.inlineLink {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
font-size: var(--text-base);
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-black);
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 3px;
|
|
||||||
transition: opacity var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.inlineLink:hover {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
max-width: 480px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styles from './Pricing.module.css';
|
|
||||||
|
|
||||||
const plans = [
|
const plans = [
|
||||||
{
|
{
|
||||||
@@ -69,12 +68,15 @@ export default function Pricing() {
|
|||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting) entry.target.classList.add(styles.visible);
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('opacity-100', 'translate-y-0');
|
||||||
|
entry.target.classList.remove('opacity-0', 'translate-y-6');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ threshold: 0.1 }
|
{ threshold: 0.1 }
|
||||||
);
|
);
|
||||||
const elements = sectionRef.current?.querySelectorAll(`.${styles.reveal}`);
|
const elements = sectionRef.current?.querySelectorAll('.reveal');
|
||||||
elements?.forEach((el) => observer.observe(el));
|
elements?.forEach((el) => observer.observe(el));
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -87,78 +89,132 @@ export default function Pricing() {
|
|||||||
<section className="section" id="pricing" ref={sectionRef} aria-labelledby="pricing-heading">
|
<section className="section" id="pricing" ref={sectionRef} aria-labelledby="pricing-heading">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${styles.reveal} ${styles.header}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out flex flex-col items-center text-center gap-4 mb-16">
|
||||||
<span className="badge badge-dark">Precios</span>
|
<span className="badge badge-dark">Precios</span>
|
||||||
<h2 id="pricing-heading" className={styles.title}>
|
<h2
|
||||||
|
id="pricing-heading"
|
||||||
|
className="text-[clamp(1.875rem,5vw,3rem)] font-black tracking-[-0.04em] leading-[1.1] text-black"
|
||||||
|
>
|
||||||
Transparente. Simple.
|
Transparente. Simple.
|
||||||
<br />
|
<br />
|
||||||
Sin sorpresas.
|
Sin sorpresas.
|
||||||
</h2>
|
</h2>
|
||||||
<p className={styles.subtitle}>
|
<p className="text-lg text-gray-600">
|
||||||
Comienza gratis. Escala cuando estés listo. Sin contratos a largo plazo.
|
Comienza gratis. Escala cuando estés listo. Sin contratos a largo plazo.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Toggle */}
|
{/* Toggle */}
|
||||||
<div className={styles.toggle}>
|
<div className="flex items-center gap-3 mt-2">
|
||||||
<span className={!annual ? styles.toggleLabelActive : styles.toggleLabel}>Mensual</span>
|
<span
|
||||||
|
className={`text-sm transition-colors duration-150 ease-out ${
|
||||||
|
!annual ? 'font-semibold text-black' : 'font-medium text-gray-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Mensual
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
className={styles.toggleSwitch}
|
className={`w-12 h-[26px] rounded-full border-none cursor-pointer relative transition-colors duration-250 ease-out ${
|
||||||
|
annual ? 'bg-black' : 'bg-gray-200'
|
||||||
|
}`}
|
||||||
onClick={() => setAnnual(!annual)}
|
onClick={() => setAnnual(!annual)}
|
||||||
aria-pressed={annual}
|
aria-pressed={annual}
|
||||||
aria-label="Cambiar a facturación anual"
|
aria-label="Cambiar a facturación anual"
|
||||||
id="pricing-toggle-btn"
|
id="pricing-toggle-btn"
|
||||||
>
|
>
|
||||||
<span className={`${styles.toggleKnob} ${annual ? styles.toggleKnobOn : ''}`} />
|
<span
|
||||||
|
className={`absolute top-[3px] left-[3px] w-5 h-5 bg-white rounded-full transition-transform duration-250 ease-out shadow-[0_1px_4px_rgba(0,0,0,0.2)] ${
|
||||||
|
annual ? 'translate-x-[22px]' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
<span className={annual ? styles.toggleLabelActive : styles.toggleLabel}>
|
<span
|
||||||
|
className={`text-sm flex items-center transition-colors duration-150 ease-out ${
|
||||||
|
annual ? 'font-semibold text-black' : 'font-medium text-gray-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
Anual
|
Anual
|
||||||
<span className={styles.saveBadge}>Ahorra 20%</span>
|
<span className="inline-block ml-2 px-2 py-[2px] bg-green-100 text-green-700 text-xs font-bold rounded-full">
|
||||||
|
Ahorra 20%
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Plans */}
|
{/* Plans */}
|
||||||
<div className={styles.grid}>
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 items-start max-w-[480px] lg:max-w-none mx-auto">
|
||||||
{plans.map((plan, i) => (
|
{plans.map((plan, i) => (
|
||||||
<article
|
<article
|
||||||
key={plan.id}
|
key={plan.id}
|
||||||
className={`${styles.reveal} ${styles.card} ${plan.highlight ? styles.cardHighlight : ''}`}
|
className={`reveal opacity-0 translate-y-6 transition-all duration-700 ease-out border rounded-2xl p-8 flex flex-col gap-6 relative group ${
|
||||||
style={{ transitionDelay: `${i * 0.1}s` }}
|
plan.highlight
|
||||||
|
? 'bg-black border-black text-white hover:shadow-[0_24px_64px_rgba(0,0,0,0.2)]'
|
||||||
|
: 'bg-white border-gray-200 hover:shadow-lg hover:-translate-y-0.5'
|
||||||
|
}`}
|
||||||
|
style={{ transitionDelay: `${i * 100}ms` }}
|
||||||
aria-label={`Plan ${plan.name}`}
|
aria-label={`Plan ${plan.name}`}
|
||||||
>
|
>
|
||||||
{plan.badge && (
|
{plan.badge && (
|
||||||
<div className={styles.popularBadge} aria-label="Plan más popular">
|
<div
|
||||||
|
className="absolute -top-[14px] left-1/2 -translate-x-1/2 bg-accent text-white text-xs font-bold px-4 py-1 rounded-full whitespace-nowrap tracking-wider"
|
||||||
|
aria-label="Plan más popular"
|
||||||
|
>
|
||||||
{plan.badge}
|
{plan.badge}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.planHeader}>
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className={styles.planName}>{plan.name}</h3>
|
<h3
|
||||||
<p className={styles.planDesc}>{plan.description}</p>
|
className={`text-2xl font-extrabold tracking-tight ${
|
||||||
|
plan.highlight ? 'text-white' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{plan.name}
|
||||||
|
</h3>
|
||||||
|
<p
|
||||||
|
className={`text-sm leading-relaxed ${
|
||||||
|
plan.highlight ? 'text-white/60' : 'text-gray-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{plan.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.priceRow}>
|
<div className="flex items-end gap-[2px]">
|
||||||
{plan.price.monthly === 0 ? (
|
{plan.price.monthly === 0 ? (
|
||||||
<span className={styles.priceValue}>Gratis</span>
|
<span className="text-[clamp(2.25rem,6vw,3.5rem)] font-black tracking-[-0.05em] leading-none">
|
||||||
|
Gratis
|
||||||
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span className={styles.priceCurrency}>$</span>
|
<span className="text-xl font-bold pb-1">$</span>
|
||||||
<span className={styles.priceValue}>
|
<span className="text-[clamp(2.25rem,6vw,3.5rem)] font-black tracking-[-0.05em] leading-none">
|
||||||
{annual ? plan.price.annual : plan.price.monthly}
|
{annual ? plan.price.annual : plan.price.monthly}
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.priceUnit}>/mes</span>
|
<span
|
||||||
|
className={`text-sm pb-1 ml-1 ${
|
||||||
|
plan.highlight ? 'text-white/50' : 'text-gray-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
/mes
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{annual && plan.price.monthly > 0 && (
|
{annual && plan.price.monthly > 0 && (
|
||||||
<p className={styles.annualNote}>
|
<p
|
||||||
|
className={`text-xs -mt-6 ${
|
||||||
|
plan.highlight ? 'text-white/40' : 'text-gray-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
Facturado anualmente — ${plan.price.annual * 12}/año
|
Facturado anualmente — ${plan.price.annual * 12}/año
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`btn btn-lg ${plan.highlight ? 'btn-accent' : 'btn-secondary'}`}
|
className={`btn btn-lg ${
|
||||||
|
plan.highlight ? 'btn-accent' : 'btn-secondary'
|
||||||
|
}`}
|
||||||
id={`pricing-${plan.id}-btn`}
|
id={`pricing-${plan.id}-btn`}
|
||||||
onClick={handleContactScroll}
|
onClick={handleContactScroll}
|
||||||
aria-label={`${plan.cta} — Plan ${plan.name}`}
|
aria-label={`${plan.cta} — Plan ${plan.name}`}
|
||||||
@@ -166,16 +222,21 @@ export default function Pricing() {
|
|||||||
{plan.cta}
|
{plan.cta}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul className={styles.featureList} role="list">
|
<ul className="list-none flex flex-col gap-3" role="list">
|
||||||
{plan.features.map((f) => (
|
{plan.features.map((f) => (
|
||||||
<li key={f} className={styles.featureItem}>
|
<li
|
||||||
|
key={f}
|
||||||
|
className={`flex items-center gap-3 text-sm ${
|
||||||
|
plan.highlight ? 'text-white/80' : 'text-gray-700'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
fill="none"
|
fill="none"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={styles.checkIcon}
|
className={`shrink-0 ${plan.highlight ? 'text-white' : 'text-black'}`}
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M3 8l3.5 3.5L13 4.5"
|
d="M3 8l3.5 3.5L13 4.5"
|
||||||
@@ -194,11 +255,11 @@ export default function Pricing() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enterprise note */}
|
{/* Enterprise note */}
|
||||||
<div className={`${styles.reveal} ${styles.enterpriseNote}`}>
|
<div className="reveal opacity-0 translate-y-6 transition-all duration-700 ease-out text-center mt-12 text-base text-gray-600">
|
||||||
<p>
|
<p>
|
||||||
¿Necesitas algo personalizado?
|
¿Necesitas algo personalizado?
|
||||||
<button
|
<button
|
||||||
className={styles.inlineLink}
|
className="bg-transparent border-none cursor-pointer font-sans text-base font-semibold text-black underline underline-offset-4 transition-opacity duration-150 ease-out hover:opacity-60"
|
||||||
onClick={handleContactScroll}
|
onClick={handleContactScroll}
|
||||||
id="pricing-enterprise-contact-btn"
|
id="pricing-enterprise-contact-btn"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user