Updated privacy policy and terms + updated contact forms
Some checks are pending
Build and Push to Docker Hub / Push Docker image to Docker Hub (push) Waiting to run
Build and Push Docker Image / build-and-push (push) Waiting to run

This commit is contained in:
nicholai 2025-09-08 18:35:11 -06:00
parent b042de5fdd
commit acce498808
91 changed files with 1829 additions and 203 deletions

View File

@ -51,6 +51,11 @@
"static/chunks/main-app.js",
"static/chunks/app/contact/page.js"
],
"/crew/page": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/chunks/app/crew/page.js"
],
"/_not-found/page": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,13 +1,14 @@
{
"/icon.svg/route": "app/icon.svg/route.js",
"/_not-found/page": "app/_not-found/page.js",
"/api/auth/[...nextauth]/route": "app/api/auth/[...nextauth]/route.js",
"/api/assets/route": "app/api/assets/route.js",
"/api/projects/route": "app/api/projects/route.js",
"/api/blog/route": "app/api/blog/route.js",
"/_not-found/page": "app/_not-found/page.js",
"/page": "app/page.js",
"/projects/page": "app/projects/page.js",
"/privacy/page": "app/privacy/page.js",
"/terms/page": "app/terms/page.js",
"/contact/page": "app/contact/page.js"
"/contact/page": "app/contact/page.js",
"/crew/page": "app/crew/page.js"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2379,6 +2379,9 @@ select {
.border-white\/20 {
border-color: rgb(255 255 255 / 0.2);
}
.border-white\/10 {
border-color: rgb(255 255 255 / 0.1);
}
.bg-accent {
background-color: rgb(var(--accent));
}
@ -2475,6 +2478,12 @@ select {
.bg-white\/50 {
background-color: rgb(255 255 255 / 0.5);
}
.bg-surface-700\/40 {
background-color: rgb(var(--surface-700) / 0.4);
}
.bg-surface-700\/30 {
background-color: rgb(var(--surface-700) / 0.3);
}
.bg-gradient-to-b {
background-image: linear-gradient(to bottom, var(--tw-gradient-stops));
}
@ -2674,6 +2683,9 @@ select {
.pt-8 {
padding-top: 2rem;
}
.pl-6 {
padding-left: 1.5rem;
}
.text-left {
text-align: left;
}

View File

@ -0,0 +1 @@
{"c":["app/layout","app/terms/page","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["app/layout","app/privacy/page","webpack"],"r":["app/_not-found/page"],"m":["(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-client-pages-loader.js?absolutePagePath=%2Fhome%2FNicholai%2FDocuments%2FDev%2Fbiohazard-vfx-1%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fbuiltin%2Fglobal-not-found.js&page=%2F_not-found%2Fpage!","(app-pages-browser)/./node_modules/next/dist/client/components/builtin/global-not-found.js","(app-pages-browser)/./node_modules/next/dist/client/components/http-access-fallback/error-fallback.js","(app-pages-browser)/./node_modules/next/dist/client/components/styles/access-error-styles.js"]}

View File

@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["app/layout","app/contact/page","webpack"],"r":[],"m":[]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"74d4c3c37c06\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6Ijs7OztBQUFBLGlFQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyIvaG9tZS9OaWNob2xhaS9Eb2N1bWVudHMvRGV2L2Jpb2hhemFyZC12ZngtMS9zcmMvYXBwL2dsb2JhbHMuY3NzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiNzRkNGMzYzM3YzA2XCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"bf8f21c059d8\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6Ijs7OztBQUFBLGlFQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyIvaG9tZS9OaWNob2xhaS9Eb2N1bWVudHMvRGV2L2Jpb2hhemFyZC12ZngtMS9zcmMvYXBwL2dsb2JhbHMuY3NzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiYmY4ZjIxYzA1OWQ4XCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"b1a015d9ee62\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6Ijs7OztBQUFBLGlFQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyIvaG9tZS9OaWNob2xhaS9Eb2N1bWVudHMvRGV2L2Jpb2hhemFyZC12ZngtMS9zcmMvYXBwL2dsb2JhbHMuY3NzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiYjFhMDE1ZDllZTYyXCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"dea05bb02898\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6Ijs7OztBQUFBLGlFQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyIvaG9tZS9OaWNob2xhaS9Eb2N1bWVudHMvRGV2L2Jpb2hhemFyZC12ZngtMS9zcmMvYXBwL2dsb2JhbHMuY3NzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiZGVhMDViYjAyODk4XCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"61f2ae1bf253\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6Ijs7OztBQUFBLGlFQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyIvaG9tZS9OaWNob2xhaS9Eb2N1bWVudHMvRGV2L2Jpb2hhemFyZC12ZngtMS9zcmMvYXBwL2dsb2JhbHMuY3NzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiNjFmMmFlMWJmMjUzXCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["app/layout","app/contact/page","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("90a754e4923d2982")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCI5MGE3NTRlNDkyM2QyOTgyXCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("0e86a91e7e559323")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCIwZTg2YTkxZTdlNTU5MzIzXCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("d4119ad37d327933")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCJkNDExOWFkMzdkMzI3OTMzXCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("36e46343a8d06bdc")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCIzNmU0NjM0M2E4ZDA2YmRjXCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("b8f36ec7a0296dca")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCJiOGYzNmVjN2EwMjk2ZGNhXCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("c2c3cf6d4fbc6f38")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCJjMmMzY2Y2ZDRmYmM2ZjM4XCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("7335094b4c8984ea")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCI3MzM1MDk0YjRjODk4NGVhXCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("7e4e6ebc75663fc6")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCI3ZTRlNmViYzc1NjYzZmM2XCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("f7f880ac56955f47")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCJmN2Y4ODBhYzU2OTU1ZjQ3XCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

View File

@ -0,0 +1,12 @@
"use strict";
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("c3b03fb16c83b498")
/******/ })();
/******/
/******/ }
)
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJpZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjay1pbnRlcm5hbDovL25leHRqcy93ZWJwYWNrLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFRoaXMgc291cmNlIHdhcyBnZW5lcmF0ZWQgYnkgTmV4dC5qcyBiYXNlZCBvZmYgb2YgdGhlIGdlbmVyYXRlZCBXZWJwYWNrIHJ1bnRpbWUuXG4vLyBUaGUgbWFwcGluZ3MgYXJlIGluY29ycmVjdC5cbi8vIFRvIGdldCB0aGUgY29ycmVjdCBsaW5lL2NvbHVtbiBtYXBwaW5ncywgdHVybiBvZmYgc291cmNlbWFwcyBpbiB5b3VyIGRlYnVnZ2VyLlxuXG5zZWxmW1wid2VicGFja0hvdFVwZGF0ZV9OX0VcIl0oXCJ3ZWJwYWNrXCIse30sXG4vKioqKioqLyBmdW5jdGlvbihfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IC8vIHdlYnBhY2tSdW50aW1lTW9kdWxlc1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2dldEZ1bGxIYXNoICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmggPSAoKSA9PiAoXCJjM2IwM2ZiMTZjODNiNDk4XCIpXG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqLyB9XG4pIl19
;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,84 @@
// File: /home/Nicholai/Documents/Dev/biohazard-vfx-1/src/app/crew/page.tsx
import * as entry from '../../../../src/app/crew/page.js'
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
type TEntry = typeof import('../../../../src/app/crew/page.js')
type SegmentParams<T extends Object = any> = T extends Record<string, any>
? { [K in keyof T]: T[K] extends string ? string | string[] | undefined : never }
: T
// Check that the entry is a valid entry
checkFields<Diff<{
default: Function
config?: {}
generateStaticParams?: Function
revalidate?: RevalidateRange<TEntry> | false
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
dynamicParams?: boolean
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
maxDuration?: number
metadata?: any
generateMetadata?: Function
viewport?: any
generateViewport?: Function
experimental_ppr?: boolean
}, TEntry, ''>>()
// Check the prop type of the entry function
checkFields<Diff<PageProps, FirstArg<TEntry['default']>, 'default'>>()
// Check the arguments and return type of the generateMetadata function
if ('generateMetadata' in entry) {
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
}
// Check the arguments and return type of the generateViewport function
if ('generateViewport' in entry) {
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
}
// Check the arguments and return type of the generateStaticParams function
if ('generateStaticParams' in entry) {
checkFields<Diff<{ params: SegmentParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
}
export interface PageProps {
params?: Promise<SegmentParams>
searchParams?: Promise<any>
}
export interface LayoutProps {
children?: React.ReactNode
params?: Promise<SegmentParams>
}
// =============
// Utility types
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
function checkFields<_ extends { [k in keyof any]: never }>() {}
// https://github.com/sindresorhus/type-fest
type Numeric = number | bigint
type Zero = 0 | 0n
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'

View File

@ -1,11 +1,11 @@
'use client'
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { Container, Section, PageHeader } from '@/components/Layouts'
import { Input, Textarea, FormGroup, FormSection } from '@/components/Forms'
import { Input, Textarea, FormSection } from '@/components/Forms'
import { Button } from '@/components/Buttons'
import ImageWithFallback from '@/components/ui/ImageWithFallback'
import Link from 'next/link'
export default function ContactPage() {
const [formData, setFormData] = useState({
@ -15,55 +15,61 @@ export default function ContactPage() {
subject: '',
message: '',
})
const [agree, setAgree] = useState(false)
const [isSubmitting, setIsSubmitting] = useState(false)
const [submitMessage, setSubmitMessage] = useState('')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setIsSubmitting(true)
setSubmitMessage('')
if (!agree) {
setSubmitMessage('Please agree to the Terms of Use and Privacy Policy.')
return
}
const accessKey = process.env.NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY
if (!accessKey) {
setSubmitMessage('Form configuration error. Missing Web3Forms access key.')
return
}
setIsSubmitting(true)
try {
const response = await fetch('https://api.web3forms.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_key:
process.env.NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY ||
'cf89ee88-fb13-4091-ac7c-96249aa34eb0',
subject: `New Contact Form Submission from Biohazard VFX`,
access_key: accessKey,
subject: 'New Contact Form Submission from Biohazard VFX',
from_name: `${formData.firstName} ${formData.lastName}`,
formatted_message: `Name: ${formData.firstName} ${formData.lastName}\nEmail: ${formData.email}\nSubject: ${formData.subject}\nMessage:\n${formData.message}`,
replyto: formData.email,
formatted_message:
`Name: ${formData.firstName} ${formData.lastName}\n` +
`Email: ${formData.email}\n` +
`Subject: ${formData.subject}\n` +
`Message:\n${formData.message}`,
firstName: formData.firstName,
lastName: formData.lastName,
email: formData.email,
user_subject: formData.subject,
message: formData.message,
// compliance/meta
consent_terms: true,
consent_privacy: true,
source: 'ContactPage',
}),
})
const result = await response.json()
if (result.success) {
setSubmitMessage('Thank you! Your message has been sent.')
setFormData({
firstName: '',
lastName: '',
email: '',
subject: '',
message: '',
})
setFormData({ firstName: '', lastName: '', email: '', subject: '', message: '' })
setAgree(false)
} else {
setSubmitMessage(
'There was an error sending your message. Please try again.'
)
setSubmitMessage('There was an error sending your message. Please try again.')
}
} catch (error) {
setSubmitMessage(
'There was an error sending your message. Please try again.'
)
} catch {
setSubmitMessage('There was an error sending your message. Please try again.')
} finally {
setIsSubmitting(false)
}
@ -72,10 +78,7 @@ export default function ContactPage() {
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setFormData((prev) => ({
...prev,
[e.target.name]: e.target.value,
}))
setFormData(prev => ({ ...prev, [e.target.name]: e.target.value }))
}
return (
@ -83,18 +86,12 @@ export default function ContactPage() {
<Section className="pt-32 ">
<Container>
<div className="min-w-2xl mx-auto">
<PageHeader
title="Connect"
subtitle="Let's bring your vision to life"
/>
<PageHeader title="Connect" subtitle="Let's bring your vision to life" />
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
<div>
<form onSubmit={handleSubmit} className="space-y-8">
<FormSection
title="Contact Information"
description="Tell us who you are"
>
<form onSubmit={handleSubmit} className="space-y-8" noValidate>
<FormSection title="Contact Information" description="Tell us who you are">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Input
label="First Name"
@ -104,6 +101,7 @@ export default function ContactPage() {
onChange={handleChange}
required
placeholder="First Name"
autoComplete="given-name"
/>
<Input
label="Last Name"
@ -113,6 +111,7 @@ export default function ContactPage() {
onChange={handleChange}
required
placeholder="Last Name"
autoComplete="family-name"
/>
</div>
<Input
@ -124,13 +123,11 @@ export default function ContactPage() {
onChange={handleChange}
required
placeholder="your@email.com"
autoComplete="email"
/>
</FormSection>
<FormSection
title="Your Message"
description="What can we help you with?"
>
<FormSection title="Your Message" description="What can we help you with?">
<Input
label="Subject"
id="subject"
@ -152,10 +149,48 @@ export default function ContactPage() {
/>
</FormSection>
{/* Notice at collection + clickwrap */}
<div className="rounded-lg border border-white/10 p-4 bg-surface-700/40 space-y-3">
<p className="text-sm text-text-secondary">
<strong>Privacy notice at collection:</strong> We collect the info you enter
(name, email, subject, message) to respond to your inquiry and operate this
form. See our{' '}
<Link href="/privacy" className="link">Privacy Policy</Link> for details.
</p>
<label className="flex items-start gap-3 text-sm text-text-secondary">
<input
type="checkbox"
className="mt-1 h-4 w-4"
checked={agree}
onChange={e => setAgree(e.target.checked)}
required
aria-describedby="consent-help"
/>
<span>
I agree to the{' '}
<Link href="/terms" className="link">Terms of Use</Link> and acknowledge the{' '}
<Link href="/privacy" className="link">Privacy Policy</Link>.
</span>
</label>
<p id="consent-help" className="sr-only">
You must agree before submitting.
</p>
{/* Honeypot field for bots per Web3Forms */}
<input
type="checkbox"
name="botcheck"
tabIndex={-1}
style={{ display: 'none' }}
aria-hidden="true"
/>
</div>
<div className="space-y-4">
<Button
type="submit"
disabled={isSubmitting}
disabled={isSubmitting || !agree}
isLoading={isSubmitting}
className="w-full"
size="lg"
@ -178,15 +213,19 @@ export default function ContactPage() {
</form>
<div className="mt-12 text-center">
<p className="text-text-muted mb-2">
We usually reply within 24 hours.
</p>
<p className="text-text-muted mb-2">We usually reply within 24 hours.</p>
<p className="text-text-muted">
Email:{' '}
<a href="mailto:contact@biohazardvfx.com" className="link">
contact@biohazardvfx.com
</a>
</p>
<p className="text-xs text-text-muted mt-2">
DMCA notices only:{' '}
<a href="mailto:Davane@biohazardvfx.com" className="link">
Davane@biohazardvfx.com
</a>
</p>
</div>
</div>
@ -200,12 +239,8 @@ export default function ContactPage() {
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent flex items-end p-6">
<div>
<h3 className="text-2xl font-display font-bold text-white mb-2">
Let's Create Together
</h3>
<p className="text-text-secondary">
Reach out to discuss your next project
</p>
<h3 className="text-2xl font-display font-bold text-white mb-2">Let's Create Together</h3>
<p className="text-text-secondary">Reach out to discuss your next project</p>
</div>
</div>
</div>

View File

@ -5,10 +5,7 @@ import { useState } from 'react'
export default function PrivacyPolicy() {
const [isExpanded, setIsExpanded] = useState(false)
const toggleExpand = () => {
setIsExpanded(!isExpanded)
}
const toggleExpand = () => setIsExpanded(!isExpanded)
return (
<PageTemplate title="Privacy Policy">
@ -16,72 +13,130 @@ export default function PrivacyPolicy() {
<p className="text-lg text-text-secondary">
Last updated: {new Date().toLocaleDateString()}
</p>
<p className="text-text-secondary">
This Privacy Policy explains how Biohazard VFX LLC (Biohazard VFX, we, us, our) collects,
uses, discloses, and protects personal information when you visit <strong>biohazardvfx.com</strong>
(the Site) or contact us.
</p>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">1. Information We Collect</h2>
<p className="text-text-secondary">
We collect information that you provide to us directly, such as when you create an account, contact us, or participate in our services. This may include your name, email address, and other contact information.
<ul className="list-disc pl-6 text-text-secondary">
<li><strong>Information you provide</strong> (e.g., name, email, company, message contents, files you choose to send).</li>
<li><strong>Automatic data</strong> (e.g., device/browser info, pages viewed, timestamps, referrers, basic diagnostics).</li>
<li><strong>Cookies & analytics</strong> used to measure traffic and performance. If we use Google Analytics 4 (GA4),
it offers region-level privacy controls; GA4 does not log or store IP addresses and drops any collected EU IP
addresses before logging. You can set your browser to block cookies.</li>
<li>We do not intentionally collect sensitive personal information.</li>
</ul>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">2. How We Use Information</h2>
<ul className="list-disc pl-6 text-text-secondary">
<li>Operate, secure, and improve the Site.</li>
<li>Respond to inquiries and communicate with you.</li>
<li>Prevent fraud/abuse; comply with law; enforce our terms.</li>
<li>Internal analytics and performance measurement.</li>
</ul>
<p className="text-text-secondary mt-4">
Where required (e.g., EEA/UK), our lawful bases include consent, contract, legal obligation, and legitimate interests.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">2. How We Use Your Information</h2>
<p className="text-text-secondary">
We use the information we collect to provide, maintain, and improve our services, to communicate with you, and to comply with legal obligations.
<h2 className="text-2xl font-bold text-white mb-4">3. Disclosures of Information</h2>
<ul className="list-disc pl-6 text-text-secondary">
<li><strong>Service providers</strong> that host, operate, or secure the Site.</li>
<li><strong>Legal/safety</strong> when required by law or to protect rights.</li>
<li><strong>Business transfers</strong> (e.g., merger, acquisition, asset sale).</li>
</ul>
<p className="text-text-secondary mt-4">
We do not sell or share personal information for cross-context behavioral advertising. If that ever changes,
we will update this Policy and provide required opt-outs (e.g., Do Not Sell or Share for California).
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">3. Information Sharing</h2>
<h2 className="text-2xl font-bold text-white mb-4">4. Retention</h2>
<p className="text-text-secondary">
We do not sell, trade, or otherwise transfer your personal information to outside parties except to trusted third parties who assist us in operating our website, conducting our business, or serving our users.
We keep personal information only as long as needed for the purposes above or as required by law, then delete or de-identify it.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">4. Data Security</h2>
<h2 className="text-2xl font-bold text-white mb-4">5. Security</h2>
<p className="text-text-secondary">
We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.
We use reasonable administrative, technical, and physical safeguards. No method of transmission or storage is 100% secure.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">5. Cookies and Tracking Technologies</h2>
<h2 className="text-2xl font-bold text-white mb-4">6. Children</h2>
<p className="text-text-secondary">
We use cookies and similar tracking technologies to track activity on our website and store certain information. You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent.
The Site is not directed to children under 13, and we do not knowingly collect their data. If you believe a child provided
personal information, contact <a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a> and we will delete it as required.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">6. Children's Privacy</h2>
<h2 className="text-2xl font-bold text-white mb-4">7. Your Privacy Rights</h2>
<p className="text-text-secondary">
Our services do not address anyone under the age of 13. We do not knowingly collect personally identifiable information from children under 13.
Depending on where you live, you may have rights to request access, correction, deletion, portability, and to opt out of certain processing.
</p>
<h3 className="text-xl font-semibold text-white mt-4 mb-2">Colorado residents (CPA)</h3>
<p className="text-text-secondary">
If the Colorado Privacy Act applies, you may submit a rights request and, if denied, <strong>appeal</strong>. Our appeal process is
conspicuously available and as easy to use as the initial request. If we deny an appeal, well tell you how to contact the Colorado Attorney General.
</p>
<h3 className="text-xl font-semibold text-white mt-4 mb-2">California residents</h3>
<p className="text-text-secondary">
If California law applies, you may have CPRA rights (e.g., to opt out of sale/sharing and to limit sensitive personal information uses).
We currently do not sell or share personal information.
</p>
<h3 className="text-xl font-semibold text-white mt-4 mb-2">EU/EEA & UK visitors</h3>
<p className="text-text-secondary">
Where GDPR applies, you may have rights including access, rectification, erasure, restriction, portability, and objection based on the lawful bases stated above.
</p>
<p className="text-text-secondary mt-4">
<strong>How to exercise your rights:</strong> Email <a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a>.
We may need to verify your identity. We will respond within the timelines required by applicable laws.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">7. Changes to This Privacy Policy</h2>
<h2 className="text-2xl font-bold text-white mb-4">8. Do Not Track & Universal Opt-Out</h2>
<p className="text-text-secondary">
We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.
We do not respond to browser Do Not Track signals at this time. Where a recognized universal opt-out mechanism is legally required
(e.g., Colorados list of Universal Opt-Out Mechanisms), we will honor it when applicable.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">8. Contact Us</h2>
<h2 className="text-2xl font-bold text-white mb-4">9. International Transfers</h2>
<p className="text-text-secondary">
If you have any questions about this Privacy Policy, please contact us at:
</p>
<p className="text-text-secondary">
Email: info@biohazardvfx.com<br />
Address: 123 VFX Street, Los Angeles, CA 90001
If we transfer personal information across borders, we use appropriate safeguards consistent with applicable law.
</p>
</section>
<div className="mt-8 p-4 bg-surface-700 rounded-lg">
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">10. Changes</h2>
<p className="text-text-secondary">
<strong>Note:</strong> This is a template and should be reviewed by a legal professional before being published on your website.
We will update this Policy as our practices or laws change. The Last updated date above reflects the latest revision.
</p>
</div>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">11. Contact</h2>
<p className="text-text-secondary">
Privacy & general inquiries: <a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a><br />
DMCA notices only: <a href="mailto:Davane@biohazardvfx.com">Davane@biohazardvfx.com</a>
</p>
</section>
</div>
</PageTemplate>
)

View File

@ -5,84 +5,173 @@ import { useState } from 'react'
export default function TermsOfService() {
const [isExpanded, setIsExpanded] = useState(false)
const toggleExpand = () => {
setIsExpanded(!isExpanded)
}
const toggleExpand = () => setIsExpanded(!isExpanded)
return (
<PageTemplate title="Terms of Service">
<PageTemplate title="Terms of Use">
<div className="prose prose-invert max-w-none">
<p className="text-lg text-text-secondary">
Last updated: {new Date().toLocaleDateString()}
</p>
<p className="text-text-secondary">
Biohazard VFX LLC (Biohazard VFX, we, us, our) operates <strong>biohazardvfx.com</strong> (the Site).
By accessing or using the Site, you agree to these Terms of Use (the Terms). If you do not agree, do not use the Site.
</p>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">1. Acceptance of Terms</h2>
<h2 className="text-2xl font-bold text-white mb-4">1. Purpose & Eligibility</h2>
<ul className="list-disc pl-6 text-text-secondary">
<li>The Site showcases our work, services, and contact information. It is for informational and promotional purposes only.</li>
<li>You must be at least 13; if under 18, use the Site only with a parent/guardian.</li>
</ul>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">2. Intellectual Property</h2>
<p className="text-text-secondary">
By accessing or using our services, you agree to be bound by these Terms of Service and all applicable laws and regulations.
If you disagree with any part of these terms, you must not use our services.
All content on the Site (including images, videos, graphics, text, logos, layouts, and code) is owned by Biohazard VFX or its licensors
and protected by copyright, trademark, and other laws. You may not copy, modify, distribute, publicly display, or create derivative works
without our prior written consent. You may link to public pages in a fair and legal manner that does not suggest sponsorship or endorsement.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">2. Use of Services</h2>
<h2 className="text-2xl font-bold text-white mb-4">3. Acceptable Use</h2>
<ul className="list-disc pl-6 text-text-secondary">
<li>No unlawful, harmful, or abusive activity.</li>
<li>No probing/scanning for vulnerabilities or attempts to bypass security.</li>
<li>No interference with the Site (e.g., overload, spam) or unauthorized automated access.</li>
<li>No scraping/harvesting/indexing, except transient caching by a standard web browser.</li>
</ul>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">4. Submissions & Unsolicited Ideas</h2>
<p className="text-text-secondary">
You agree to use our services only for lawful purposes and in a manner that does not infringe upon the rights of others or restrict their use of the services.
Do not send confidential information through the Site. No fiduciary or confidentiality obligations arise from unsolicited submissions.
If you send feedback or materials, you grant us a worldwide, perpetual, irrevocable, royalty-free license to use, reproduce, adapt, publish,
and display them for our business (excluding your personal data, which is handled under our Privacy Policy).
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">3. Intellectual Property</h2>
<h2 className="text-2xl font-bold text-white mb-4">5. Third-Party Links</h2>
<p className="text-text-secondary">
All content, features, and functionality on our services are owned by Biohazard VFX or its licensors and are protected by international copyright, trademark, patent, trade secret, and other intellectual property or proprietary rights laws.
The Site may link to third-party websites. We do not control or endorse those sites and are not responsible for their content or policies.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">4. User Accounts</h2>
<h2 className="text-2xl font-bold text-white mb-4">6. No Warranties</h2>
<p className="text-text-secondary">
When you create an account with us, you must provide accurate and complete information. You are responsible for maintaining the confidentiality of your account and password and for restricting access to your computer.
THE SITE AND ALL CONTENT ARE PROVIDED AS IS AND AS AVAILABLE. TO THE MAXIMUM EXTENT PERMITTED BY LAW,
WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
TITLE, AND NON-INFRINGEMENT.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">5. Termination</h2>
<h2 className="text-2xl font-bold text-white mb-4">7. Limitation of Liability</h2>
<p className="text-text-secondary">
We may terminate or suspend your account immediately, without prior notice or liability, for any reason whatsoever, including without limitation if you breach the Terms.
TO THE MAXIMUM EXTENT PERMITTED BY LAW, BIOHAZARD VFX WILL NOT BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL,
CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOST PROFITS, REVENUE, DATA, OR GOODWILL, ARISING OUT OF
OR RELATING TO YOUR USE OR INABILITY TO USE THE SITE. OUR TOTAL LIABILITY FOR ANY CLAIM RELATING TO THE SITE
WILL NOT EXCEED <strong>$100</strong>.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">6. Limitation of Liability</h2>
<h2 className="text-2xl font-bold text-white mb-4">8. Indemnity</h2>
<p className="text-text-secondary">
In no event shall Biohazard VFX, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from (i) your access to or use of or inability to access or use the services; (ii) any conduct or content of any third party on the service; (iii) any content obtained from the service; or (iv) unauthorized access, use or alteration of your transmissions or content.
You will defend, indemnify, and hold harmless Biohazard VFX and our members, directors, officers, employees, and agents
from and against claims, damages, liabilities, costs, and expenses (including reasonable attorneys fees) arising from your
violation of these Terms or misuse of the Site.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">7. Changes to Terms</h2>
<h2 className="text-2xl font-bold text-white mb-4">9. DMCA / Copyright Complaints</h2>
<p className="text-text-secondary">
We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material, we will provide at least 30 days' notice prior to any new terms taking effect.
If you believe content on the Site infringes your copyright, send a notice to our DMCA Agent:
</p>
<p className="text-text-secondary">
<strong>DMCA Agent:</strong> Davane (Executive Producer)<br />
<strong>Email:</strong> <a href="mailto:Davane@biohazardvfx.com">Davane@biohazardvfx.com</a>
</p>
<p className="text-text-secondary">
Your notice must include the information required by 17 U.S.C. §512(c)(3). We may remove content and terminate repeat infringers
in appropriate circumstances.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">8. Contact Us</h2>
<h2 className="text-2xl font-bold text-white mb-4">10. Privacy</h2>
<p className="text-text-secondary">
If you have any questions about these Terms, please contact us at:
</p>
<p className="text-text-secondary">
Email: info@biohazardvfx.com<br />
Address: 123 VFX Street, Los Angeles, CA 90001
Your use of the Site is also governed by our Privacy Policy, incorporated here by reference.
</p>
</section>
<div className="mt-8 p-4 bg-surface-700 rounded-lg">
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">11. Changes; Termination</h2>
<p className="text-text-secondary">
<strong>Note:</strong> This is a template and should be reviewed by a legal professional before being published on your website.
We may update these Terms at any time by posting a revised version on this page (effective upon posting). We may modify or
discontinue the Site, or suspend/terminate access, at our discretion.
</p>
</div>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">12. Governing Law; Venue</h2>
<p className="text-text-secondary">
These Terms are governed by the laws of the State of Colorado, without regard to conflicts-of-law principles. Subject to Section 13,
the exclusive venue for disputes will be the state or federal courts located in El Paso County or Denver County, Colorado,
and you consent to personal jurisdiction there.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">13. Arbitration & Class-Action Waiver (Consumers)</h2>
<p className="text-text-secondary">
<strong>Binding Arbitration.</strong> Any dispute or claim between you and us arising out of or relating to these Terms or the Site
will be resolved by final and binding arbitration administered by the American Arbitration Association (AAA) under its
Consumer Arbitration Rules. The arbitration will occur in Denver, Colorado, before a single arbitrator.
</p>
<p className="text-text-secondary">
<strong>Class Waiver.</strong> You and we agree to arbitrate only on an individual basis; no class or representative proceedings.
</p>
<p className="text-text-secondary">
<strong>Small-Claims Option.</strong> Either party may bring an individual action in a court with small-claims jurisdiction.
</p>
<p className="text-text-secondary">
<strong>30-Day Opt-Out.</strong> You may opt out of this arbitration provision by emailing
{' '}<a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a>{' '}
with the subject line Arbitration Opt-Out within 30 days of your first use of the Site after the effective date of these Terms.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">14. Export & Sanctions</h2>
<p className="text-text-secondary">
You agree to comply with all U.S. export control and sanctions laws and not to use the Site in violation of such laws.
</p>
</section>
<section className="mb-8">
<h2 className="text-2xl font-bold text-white mb-4">15. Miscellaneous</h2>
<ul className="list-disc pl-6 text-text-secondary">
<li>These Terms are the entire agreement regarding your use of the Site and supersede prior communications.</li>
<li>If any provision is unenforceable, the remainder stays in effect.</li>
<li>Our failure to enforce a provision is not a waiver.</li>
<li>You may not assign these Terms without our consent; we may assign them in connection with a merger, acquisition, or asset sale.</li>
</ul>
</section>
<section className="mb-2">
<h2 className="text-2xl font-bold text-white mb-4">16. Contact</h2>
<p className="text-text-secondary">
General inquiries: <a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a><br />
DMCA notices only: <a href="mailto:Davane@biohazardvfx.com">Davane@biohazardvfx.com</a>
</p>
</section>
</div>
</PageTemplate>
)

View File

@ -10,24 +10,58 @@ export default function Footer() {
const [email, setEmail] = useState('')
const [firstName, setFirstName] = useState('')
const [message, setMessage] = useState('')
const [agree, setAgree] = useState(false)
const [isSubmitting, setIsSubmitting] = useState(false)
const [submitMessage, setSubmitMessage] = useState('')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setSubmitMessage('')
if (!agree) {
setSubmitMessage('Please agree to the Terms of Use and Privacy Policy.')
return
}
const accessKey = process.env.NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY
if (!accessKey) {
setSubmitMessage('Form configuration error. Missing Web3Forms access key.')
return
}
setIsSubmitting(true)
// Simulate form submission
setTimeout(() => {
setSubmitMessage("Thank you! We'll be in touch soon.")
setEmail('')
setFirstName('')
setMessage('')
try {
const res = await fetch('https://api.web3forms.com/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_key: accessKey,
subject: 'Footer inquiry from Biohazard VFX',
from_name: firstName || 'Website Visitor',
replyto: email,
firstName,
email,
message,
consent_terms: true,
consent_privacy: true,
source: 'FooterForm'
})
})
const result = await res.json()
if (result.success) {
setSubmitMessage("Thank you! We'll be in touch soon.")
setEmail('')
setFirstName('')
setMessage('')
setAgree(false)
} else {
setSubmitMessage('There was an error sending your message. Please try again.')
}
} catch {
setSubmitMessage('There was an error sending your message. Please try again.')
} finally {
setIsSubmitting(false)
// Clear success message after 5 seconds
setTimeout(() => setSubmitMessage(''), 5000)
}, 1000)
}
}
return (
@ -36,34 +70,21 @@ export default function Footer() {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-12">
{/* Company Info */}
<div className="lg:col-span-1">
<h3 className="text-2xl font-display text-white mb-4">
Biohazard VFX
</h3>
<h3 className="text-2xl font-display text-white mb-4">Biohazard VFX</h3>
<p className="text-text-muted text-sm mb-4">
Global visual effects studio delivering world-class VFX
supervision, 3D animation, and post-production services.
</p>
<p className="text-text-muted text-sm">
We usually reply within 24 hours.
Global visual effects studio delivering world-class VFX supervision, 3D animation, and post-production services.
</p>
<p className="text-text-muted text-sm">We usually reply within 24 hours.</p>
</div>
{/* Quick Links */}
<div>
<h4 className="text-lg font-semibold text-white mb-4">Services</h4>
<nav className="space-y-3">
<Link href="/project" className="block link-muted text-sm">
VFX Supervision
</Link>
<Link href="/project" className="block link-muted text-sm">
3D Animation
</Link>
<Link href="/project" className="block link-muted text-sm">
Post-Production
</Link>
<Link href="/project" className="block link-muted text-sm">
Motion Graphics
</Link>
<Link href="/project" className="block link-muted text-sm">VFX Supervision</Link>
<Link href="/project" className="block link-muted text-sm">3D Animation</Link>
<Link href="/project" className="block link-muted text-sm">Post-Production</Link>
<Link href="/project" className="block link-muted text-sm">Motion Graphics</Link>
</nav>
</div>
@ -71,27 +92,26 @@ export default function Footer() {
<div>
<h4 className="text-lg font-semibold text-white mb-4">Company</h4>
<nav className="space-y-3">
<Link href="/" className="block link-muted text-sm">
Home
</Link>
<Link href="/crew" className="block link-muted text-sm">
Our Crew
</Link>
<Link href="/blog" className="block link-muted text-sm">
Blog
</Link>
<Link href="/contact" className="block link-muted text-sm">
Contact
</Link>
<Link href="/" className="block link-muted text-sm">Home</Link>
<Link href="/crew" className="block link-muted text-sm">Our Crew</Link>
<Link href="/blog" className="block link-muted text-sm">Blog</Link>
<Link href="/contact" className="block link-muted text-sm">Contact</Link>
</nav>
</div>
{/* Contact Form */}
{/* Contact/Consent Form */}
<div>
<h4 className="text-lg font-semibold text-white mb-4">
Get in Touch
</h4>
<form onSubmit={handleSubmit} className="space-y-3">
<h4 className="text-lg font-semibold text-white mb-4">Get in Touch</h4>
<form onSubmit={handleSubmit} className="space-y-3" noValidate>
{/* Honeypot for bots */}
<input
type="checkbox"
name="botcheck"
tabIndex={-1}
style={{ display: 'none' }}
aria-hidden="true"
/>
<Input
type="text"
placeholder="First Name *"
@ -99,6 +119,9 @@ export default function Footer() {
onChange={(e) => setFirstName(e.target.value)}
required
className="text-sm"
autoComplete="given-name"
name="firstName"
id="footer-firstName"
/>
<Input
type="email"
@ -107,6 +130,9 @@ export default function Footer() {
onChange={(e) => setEmail(e.target.value)}
required
className="text-sm"
autoComplete="email"
name="email"
id="footer-email"
/>
<Textarea
placeholder="Message"
@ -114,10 +140,35 @@ export default function Footer() {
onChange={(e) => setMessage(e.target.value)}
rows={3}
className="text-sm"
name="message"
id="footer-message"
/>
<div className="rounded-lg border border-white/10 p-3 bg-surface-700/30">
<p className="text-xs text-text-muted mb-2">
We collect the info you enter to respond to your inquiry and operate this form.
See our <Link href="/privacy" className="link">Privacy Policy</Link> for details.
</p>
<label className="flex items-start gap-3 text-xs text-text-muted">
<input
type="checkbox"
className="mt-0.5 h-4 w-4"
checked={agree}
onChange={(e) => setAgree(e.target.checked)}
required
aria-describedby="footer-consent-help"
/>
<span>
I agree to the <Link href="/terms" className="link">Terms of Use</Link> and acknowledge the{' '}
<Link href="/privacy" className="link">Privacy Policy</Link>.
</span>
</label>
<p id="footer-consent-help" className="sr-only">You must agree before submitting.</p>
</div>
<Button
type="submit"
disabled={isSubmitting}
disabled={isSubmitting || !agree}
isLoading={isSubmitting}
size="sm"
className="w-full"
@ -125,7 +176,11 @@ export default function Footer() {
Send
</Button>
{submitMessage && (
<p className="text-green-400 text-xs text-center">
<p
className={`text-xs text-center ${
submitMessage.includes('Thank you') ? 'text-green-400' : 'text-red-400'
}`}
>
{submitMessage}
</p>
)}
@ -133,7 +188,7 @@ export default function Footer() {
</div>
</div>
{/* Social Links & Copyright */}
{/* Social Links & Legal */}
<div className="border-t border-surface-500 pt-8">
<div className="flex flex-col md:flex-row justify-between items-center gap-4">
<div className="flex items-center gap-4">
@ -142,13 +197,11 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
className="text-text-muted hover:text-accent transition-colors"
aria-label="Instagram"
title="Instagram"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z" />
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 5c-3.859 0-7 3.141-7 7 0 3.859 3.141 7 7 7s7-3.141 7-7c0-3.859-3.141-7-7-7zm6.406.52a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z" />
</svg>
</a>
<a
@ -156,12 +209,10 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
className="text-text-muted hover:text-accent transition-colors"
aria-label="Vimeo"
title="Vimeo"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M22.875 10.063c-2.442 5.217-8.337 12.319-12.063 12.319-3.672 0-4.203-7.831-6.208-13.043-.987-2.565-1.624-1.814-3.474-.281L0 8.019c2.698-2.435 5.394-5.391 7.396-5.553 3.162-.242 3.487 2.831 4.024 5.479.699 3.463 1.809 8.845 2.801 8.845.796 0 2.289-3.313 2.428-4.516.222-1.853-1.512-1.879-2.971-1.271C17.503-2.071 22.875 4.44 22.875 10.063z" />
</svg>
</a>
@ -170,12 +221,10 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
className="text-text-muted hover:text-accent transition-colors"
aria-label="YouTube"
title="YouTube"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg>
</a>
@ -184,12 +233,13 @@ export default function Footer() {
<div className="flex flex-col md:flex-row items-center gap-4 text-sm text-text-muted">
<p>© {new Date().getFullYear()} Biohazard VFX. All rights reserved.</p>
<div className="flex gap-4">
<Link href="/privacy" className="link-muted">
Privacy Policy
</Link>
<Link href="/terms" className="link-muted">
Terms of Service
</Link>
<Link href="/privacy" className="link-muted">Privacy Policy</Link>
<Link href="/terms" className="link-muted">Terms of Use</Link>
<a href="mailto:Davane@biohazardvfx.com" className="link-muted" title="DMCA Agent">
DMCA
</a>
{/* If you ever sell/share data under CPRA, expose an opt-out link below */}
{/* <Link href="/privacy-choices" className="link-muted">Your Privacy Choices</Link> */}
</div>
</div>
</div>