diff --git a/content/news/2026-03-14-german-open-third-place.md b/content/news/2026-03-14-german-open-third-place.md new file mode 100644 index 0000000..6b03a82 --- /dev/null +++ b/content/news/2026-03-14-german-open-third-place.md @@ -0,0 +1,15 @@ +--- +title: "3rd place at RoboCup German Open 2026" +date: 2026-03-14 +summary: "We got 3rd in the SPL at German Open, two weeks after first getting our robots." +author: "Team whIRLwind" +tags: ["competition", "german-open", "2026"] +--- + +We got 3rd in the SPL at the 2026 RoboCup German Open. + +We had the robots for two weeks before the tournament. In that time we wrote everything that ran on them from scratch: the vision stack, the behaviours, and the RL policies for walking and ball handling. No code reused from previous years. + +Two weeks is not a lot of time to get an RL policy to behave on a real robot, and it shows in some of our games. But it also held up well enough in others to put us on the podium, which we're pretty happy with given where we started. + +Thanks to everyone on the team, and to the other teams and the organisers for the tournament. diff --git a/eslint.config.mjs b/eslint.config.mjs index 719cea2..1f5006b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,16 +1,9 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); +import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; +import nextTypeScript from "eslint-config-next/typescript"; const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), + ...nextCoreWebVitals, + ...nextTypeScript, { ignores: [ "node_modules/**", @@ -18,6 +11,7 @@ const eslintConfig = [ "out/**", "build/**", "next-env.d.ts", + "next_version/**", ], }, ]; diff --git a/package.json b/package.json index 79229d5..8b63713 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,15 @@ "lint": "eslint" }, "dependencies": { + "gray-matter": "^4.0.3", "next": "16.1.1", "prettier": "^3.7.4", "react": "19.2.3", "react-dom": "19.2.3", + "remark": "^15.0.1", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", + "remark-html": "^16.0.1", "zod": "^4.3.5" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83f4b97..feacdfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 next: specifier: 16.1.1 version: 16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.97.2) @@ -20,6 +23,18 @@ importers: react-dom: specifier: 19.2.3 version: 19.2.3(react@19.2.3) + remark: + specifier: ^15.0.1 + version: 15.0.1 + remark-breaks: + specifier: ^4.0.0 + version: 4.0.0 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + remark-html: + specifier: ^16.0.1 + version: 16.0.1 zod: specifier: ^4.3.5 version: 4.3.5 @@ -596,15 +611,27 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@25.0.3': resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} @@ -616,6 +643,9 @@ packages: '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@typescript-eslint/eslint-plugin@8.52.0': resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -675,6 +705,9 @@ packages: resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} cpu: [arm] @@ -787,6 +820,9 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -845,6 +881,9 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -886,10 +925,22 @@ packages: caniuse-lite@1.0.30001762: resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -904,6 +955,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -949,6 +1003,9 @@ packages: supports-color: optional: true + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -960,6 +1017,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} @@ -969,6 +1030,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1027,6 +1091,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-config-next@16.1.1: resolution: {integrity: sha512-55nTpVWm3qeuxoQKLOjQVciKZJUphKrNM0fCcQHAIOGl6VFXgaqeMfv0aKJhs7QtcnlAPhNVqsqRfRjeKBPIUA==} peerDependencies: @@ -1127,6 +1195,11 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -1143,6 +1216,13 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1251,6 +1331,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -1278,12 +1362,24 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1342,6 +1438,10 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1374,6 +1474,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1427,6 +1531,10 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true @@ -1461,6 +1569,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -1549,6 +1661,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1559,14 +1674,140 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-newline-to-break@2.0.0: + resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -1722,6 +1963,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1753,6 +1997,24 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + remark-breaks@4.0.0: + resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-html@16.0.1: + resolution: {integrity: sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1796,6 +2058,10 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -1849,6 +2115,12 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -1879,6 +2151,13 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -1923,6 +2202,12 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -1974,6 +2259,24 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -1986,6 +2289,12 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -2027,6 +2336,9 @@ packages: zod@4.3.5: resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -2512,12 +2824,26 @@ snapshots: tslib: 2.8.1 optional: true + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/estree@1.0.8': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 @@ -2530,6 +2856,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/unist@3.0.3': {} + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -2621,6 +2949,8 @@ snapshots: '@typescript-eslint/types': 8.52.0 eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.1': {} + '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true @@ -2697,6 +3027,10 @@ snapshots: dependencies: color-convert: 2.0.1 + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + argparse@2.0.1: {} aria-query@5.3.2: {} @@ -2780,6 +3114,8 @@ snapshots: axobject-query@4.1.0: {} + bail@2.0.2: {} + balanced-match@1.0.2: {} baseline-browser-mapping@2.9.11: {} @@ -2826,11 +3162,19 @@ snapshots: caniuse-lite@1.0.30001762: {} + ccount@2.0.1: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -2843,6 +3187,8 @@ snapshots: color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + concat-map@0.0.1: {} convert-source-map@2.0.0: {} @@ -2883,6 +3229,10 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -2897,11 +3247,17 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + dequal@2.0.3: {} + detect-libc@1.0.3: optional: true detect-libc@2.1.2: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -3026,6 +3382,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-next@16.1.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.1.1 @@ -3069,7 +3427,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: @@ -3091,7 +3449,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -3217,6 +3575,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -3229,6 +3589,12 @@ snapshots: esutils@2.0.3: {} + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -3341,6 +3707,13 @@ snapshots: graceful-fs@4.2.11: {} + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -3363,12 +3736,38 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.3.1 + unist-util-position: 5.0.0 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hermes-estree@0.25.1: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 + html-void-elements@3.0.0: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -3432,6 +3831,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-extendable@0.1.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -3461,6 +3862,8 @@ snapshots: is-number@7.0.0: {} + is-plain-obj@4.1.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -3517,6 +3920,11 @@ snapshots: js-tokens@4.0.0: {} + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -3546,6 +3954,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@6.0.3: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -3612,6 +4022,8 @@ snapshots: lodash.merge@4.6.2: {} + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -3624,10 +4036,322 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-table@3.0.4: {} + math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-newline-to-break@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-find-and-replace: 3.0.2 + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + merge2@1.4.1: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -3786,6 +4510,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-information@7.1.0: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -3821,6 +4547,55 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + remark-breaks@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-newline-to-break: 2.0.0 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-html@16.0.1: + dependencies: + '@types/mdast': 4.0.4 + hast-util-sanitize: 5.0.2 + hast-util-to-html: 9.0.5 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remark@15.0.1: + dependencies: + '@types/mdast': 4.0.4 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -3872,6 +4647,11 @@ snapshots: scheduler@0.27.0: {} + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + semver@6.3.1: {} semver@7.7.3: {} @@ -3966,6 +4746,10 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + + sprintf-js@1.0.3: {} + stable-hash@0.0.5: {} stop-iteration-iterator@1.1.0: @@ -4023,6 +4807,13 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-bom-string@1.0.0: {} + strip-bom@3.0.0: {} strip-json-comments@3.1.1: {} @@ -4053,6 +4844,10 @@ snapshots: dependencies: is-number: 7.0.0 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -4125,6 +4920,39 @@ snapshots: undici-types@7.16.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -4159,6 +4987,16 @@ snapshots: dependencies: punycode: 2.3.1 + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -4215,3 +5053,5 @@ snapshots: zod: 4.3.5 zod@4.3.5: {} + + zwitch@2.0.4: {} diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx index 04c97f4..2979c31 100644 --- a/src/app/contact/page.tsx +++ b/src/app/contact/page.tsx @@ -1,5 +1,7 @@ import LinkButton from "@/components/LinkButton"; import type { Metadata } from "next"; +import PageHero from "@/components/site/PageHero"; +import { contactTopics, siteContact } from "@/lib/site-content"; export const metadata: Metadata = { title: "Contact | Team whIRLwind", @@ -8,116 +10,99 @@ export const metadata: Metadata = { export default function ContactPage() { return ( -
-
-
-

Contact

-
- -
-
-
- Membership update -

- Applications temporarily closed -

-

- We're pausing new member applications while we continue - ramping up the team. We'll share an update on our channels - once we're ready to welcome new teammates again. -

-
- -
-
+
+ + Get in touch. + + } + description="Email us about sponsorships, research, demos, event invites, or press." + metrics={[ + { label: "Base", value: "Science Park" }, + { label: "Lab", value: "UvA IRL" }, + { label: "Status", value: "Applications paused" }, + ]} + actions={ + <> + + {siteContact.email} + + + + } + aside={ +
+

Membership update

+

Applications are closed for now.

+ + We'll post on our channels when we open them again. + +
+ } + /> -
-

Reach the team

+
+
+
+
+

Contact

+

Email

+ + {siteContact.email} +

- For collaborations, sponsorships, press, or general questions, - drop us a line and we'll connect you with the right person. + We'll get it to the right person.

-
- -
-
+ -
-

How we can help

-
-
-

Research & demos

-

- Invite us to showcase humanoid robotics work or speak with - your students, lab, or community. -

-
-
-

Sponsorship & support

-

- Share how you'd like to collaborate and we'll - explore ways to partner on upcoming competitions and events. -

-
-
-

Media & press

-

- Tell us your deadline and angle so we can coordinate - spokespeople, quotes, and assets. -

-
+
+

Topics

+
+ {contactTopics.map((topic) => ( +
+

{topic.title}

+

{topic.description}

+
+ ))}
-
+
-
-
-
+ + ); } diff --git a/src/app/globals.scss b/src/app/globals.scss index bb75baf..f034cd2 100644 --- a/src/app/globals.scss +++ b/src/app/globals.scss @@ -1,45 +1,33 @@ -@use "sass:math"; @use "tailwindcss"; -/* Whirlwind Robotics - global theme */ :root { - /* Text */ - --ink: #eaf1ff; - --ink-dim: #c4d2ff; - --ink-muted: #9fb0d9; - - /* Brand - Blue/Orange */ - --blue-500: #040069; /* deep blue */ - --blue-400: #050087; - --blue-300: #0700b8; - --orange-500: #f26722; /* vibrant orange */ - --orange-400: #ff7c3d; - - /* Backgrounds */ - --bg-900: #0b1020; /* deep blue-black */ - --bg-800: #121a2e; /* section panels */ - --bg-700: #1b2742; - - /* Gradients */ - --grad-a: #4cc9f0; /* cyan highlight */ - --grad-b: #4361ee; /* indigo highlight */ - --grad-c: #f97316; /* orange accent */ - - --shadow: 0 10px 30px rgba(0, 0, 0, 0.35), 0 2px 10px rgba(0, 0, 0, 0.2); - - /* Next.js font variables from layout */ - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); - - --font-headline: var(--font-google); + --ww-navy-deep: #08172f; + --ww-navy: #102447; + --ww-navy-soft: #17325f; + --ww-surface: rgba(7, 20, 41, 0.74); + --ww-surface-strong: rgba(10, 24, 51, 0.92); + --ww-orange: #e8652b; + --ww-orange-soft: #f39a6a; + --ww-cream: #f5f2ec; + --ww-off-white: #fcfaf6; + --ww-muted: rgba(245, 242, 236, 0.62); + --ww-muted-strong: rgba(245, 242, 236, 0.82); + --ww-line: rgba(245, 242, 236, 0.12); + --ww-line-strong: rgba(245, 242, 236, 0.22); + --ww-shadow: 0 24px 64px rgba(0, 0, 0, 0.28); + --font-display: "Space Grotesk", "Avenir Next", "Trebuchet MS", sans-serif; + --font-body: "Inter", "Avenir Next", "Segoe UI", sans-serif; + --font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; } * { box-sizing: border-box; } + html { min-height: 100%; - background-color: #0b183d; + scroll-behavior: smooth; + background: var(--ww-navy-deep); } body { @@ -47,90 +35,86 @@ body { margin: 0; display: flex; flex-direction: column; - position: relative; -} - -body { - font-family: - ui-sans-serif, - system-ui, - -apple-system, - Segoe UI, - Roboto, - Helvetica, - Arial, - "Apple Color Emoji", - "Segoe UI Emoji"; - color: var(--ink); - background-color: transparent; + color: var(--ww-cream); + font-family: var(--font-body); + background: transparent; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } +body.mobile-nav-open { + overflow: hidden; +} + +a { + color: inherit; +} + +button, +input, +textarea, +select { + font: inherit; +} + +img { + max-width: 100%; +} + +#team, +#results { + scroll-margin-top: 7rem; +} + .site-background { position: fixed; inset: 0; z-index: -1; - background: linear-gradient(180deg, #0b183d 0%, #163068 100%); + background: + radial-gradient(circle at top right, rgba(232, 101, 43, 0.16), transparent 28%), + radial-gradient(circle at top left, rgba(79, 118, 191, 0.16), transparent 32%), + linear-gradient(180deg, #08172f 0%, #0f2348 52%, #08152b 100%); pointer-events: none; } -/* App shell */ -.site-header { - position: sticky; - top: 0; - z-index: 50; - background: rgba(11, 24, 61, 0.75); - backdrop-filter: blur(12px) saturate(150%); - -webkit-backdrop-filter: blur(12px) saturate(150%); - border-bottom: none; - padding-top: calc(1rem + env(safe-area-inset-top, 0px)); - padding-bottom: 2rem; - mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 100%); - -webkit-mask-image: linear-gradient( - to bottom, - black 0%, - black 50%, - transparent 100% - ); -} - -/* The mask on .site-header provides the fade, so disable the old overlay */ -.site-header::after { - content: none; -} - .site-main { flex: 1 0 auto; } -.site-footer { - position: relative; - z-index: 0; /* create context so ::before can sit behind */ - flex-shrink: 0; - padding-bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px)); +.site-container { + width: min(1180px, calc(100% - 2rem)); + margin: 0 auto; } -/* Remove external fade overlay to avoid covering preceding content */ -.site-footer::before { - content: none; +.site-header { + position: sticky; + top: 0; + z-index: 50; + background: rgba(8, 23, 47, 0.72); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + border-bottom: 1px solid var(--ww-line); } -.footer-inner { +.nav { display: flex; - gap: 1rem; align-items: center; - justify-content: flex-start; - text-align: left; - padding-block: 1.25rem; - color: var(--ink-muted); + justify-content: space-between; + gap: 1rem; + padding: 1rem 0; } -.nav { - display: flex; +.brand { + display: inline-flex; align-items: center; - justify-content: space-between; - padding-block: 1.1rem; + flex-shrink: 0; + text-decoration: none; +} + +.logo { + width: auto; + height: 3.4rem; + display: block; } .nav-list-container { @@ -139,1196 +123,1350 @@ body { gap: 1rem; } -.nav-toggle { - display: none; +.nav-list { + display: flex; + align-items: center; + gap: 0.35rem; + list-style: none; + margin: 0; + padding: 0; +} + +.link-button { + display: inline-flex; align-items: center; justify-content: center; - width: 2.75rem; - height: 2.75rem; - border-radius: 9999px; - border: 1px solid rgba(96, 165, 250, 0.28); - background: linear-gradient( - 180deg, - rgba(96, 165, 250, 0.08), - rgba(96, 165, 250, 0.03) - ); - color: var(--ink); + min-height: 2.85rem; + border-radius: 999px; + text-decoration: none; transition: - border-color 0.2s ease, - background 0.2s ease, - transform 0.1s ease; + transform 0.18s ease, + background-color 0.18s ease, + border-color 0.18s ease, + color 0.18s ease, + box-shadow 0.18s ease; +} + +.link-button:hover, +.link-button:focus-visible, +.utility-button:hover, +.utility-button:focus-visible, +.social-card:hover, +.social-card:focus-visible, +.sponsor-card__link:hover, +.sponsor-card__link:focus-visible { + transform: translateY(-1px); +} + +.link-button:focus-visible, +.utility-button:focus-visible, +.social-card:focus-visible, +.sponsor-card__link:focus-visible, +.detail-card__link:focus-visible { + outline: 2px solid rgba(243, 154, 106, 0.85); + outline-offset: 2px; +} + +.link-button__label { + position: relative; + z-index: 1; +} + +.link-button--nav { + padding: 0.72rem 1rem; + color: var(--ww-muted-strong); + font-size: 0.94rem; + font-weight: 600; +} + +.link-button--nav.is-active { + color: var(--ww-orange-soft); + background: rgba(232, 101, 43, 0.08); +} + +.link-button--nav:hover { + color: var(--ww-off-white); + background: rgba(245, 242, 236, 0.06); +} + +.link-button--primary, +.link-button--secondary { + padding: 0.95rem 1.4rem; + font-weight: 600; + letter-spacing: 0.01em; +} + +.link-button--primary { + color: #fff; + background: linear-gradient(135deg, var(--ww-orange) 0%, #cf5421 100%); + box-shadow: 0 16px 36px rgba(232, 101, 43, 0.22); +} + +.link-button--secondary { + color: var(--ww-off-white); + border: 1px solid var(--ww-line-strong); + background: rgba(245, 242, 236, 0.03); +} + +.link-button--primary:hover { + box-shadow: 0 20px 40px rgba(232, 101, 43, 0.26); +} + +.link-button--secondary:hover { + border-color: rgba(243, 154, 106, 0.55); + background: rgba(245, 242, 236, 0.06); +} + +.link-button--inline { + min-height: auto; + padding: 0; + color: var(--ww-orange-soft); + font-weight: 600; } -.nav-toggle:hover { - border-color: rgba(251, 146, 60, 0.45); +.link-button--inline:hover { + color: var(--ww-off-white); } -.nav-toggle:active { - transform: translateY(1px); +.nav-cta--mobile { + display: none; } -.nav-toggle:focus-visible { - outline: none; - box-shadow: 0 0 0 3px rgba(251, 146, 60, 0.15); +.nav-toggle { + display: none; + align-items: center; + justify-content: center; + width: 2.9rem; + height: 2.9rem; + padding: 0; + border: 1px solid var(--ww-line-strong); + border-radius: 999px; + color: var(--ww-cream); + background: rgba(245, 242, 236, 0.04); } .nav-toggle__icon { position: relative; - width: 20px; - height: 14px; display: inline-flex; flex-direction: column; justify-content: space-between; + width: 1.15rem; + height: 0.9rem; } .nav-toggle__icon span { display: block; height: 2px; + border-radius: 999px; background: currentColor; - border-radius: 9999px; transition: - transform 0.25s ease, - opacity 0.25s ease; + transform 0.2s ease, + opacity 0.2s ease; } .site-header--open .nav-toggle__icon span:nth-child(1) { transform: translateY(6px) rotate(45deg); } + .site-header--open .nav-toggle__icon span:nth-child(2) { opacity: 0; } + .site-header--open .nav-toggle__icon span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); } -.nav-toggle__icon span:nth-child(2) { - transform-origin: left center; + +.home-page, +.page-shell { + padding-bottom: 2rem; +} + +.home-hero, +.page-hero { + position: relative; + padding: clamp(2.75rem, 6vw, 5.5rem) 0 1.75rem; } -.brand { - display: flex; +.home-hero__grid, +.page-hero__grid { + display: grid; + grid-template-columns: minmax(0, 1.05fr) minmax(320px, 0.95fr); + gap: clamp(2rem, 5vw, 4rem); align-items: center; - gap: 0.6rem; - text-decoration: none; -} -.logo { - height: 4em; - width: auto; - display: block; } -.brand-text { + +.home-hero__title, +.page-hero__title, +.section-intro__title, +.contact-banner__title { + margin: 0.7rem 0 1rem; + font-family: var(--font-display); font-weight: 700; - letter-spacing: 0.2px; - color: var(--ink); + line-height: 0.94; + letter-spacing: -0.04em; } -.nav-list { - display: flex; - gap: 1rem; - list-style: none; - margin: 0; - padding: 0; -} -.nav-link { - --nav-link-active: 0; - --nav-link-transition: 0.25s; - --nav-link-spin: 1.25s; - --nav-link-stroke: 0.16rem; - --nav-link-play-state: paused; - --nav-link-angle: 0deg; - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.4rem; - padding: 0.45rem 0.95rem; - border-radius: 9999px; - color: var(--ink); - background: transparent; - text-decoration: none; - font-weight: 600; - white-space: nowrap; - overflow: hidden; - isolation: isolate; - transition: - color var(--nav-link-transition), - transform var(--nav-link-transition), - text-shadow var(--nav-link-transition); +.home-hero__title { + font-size: clamp(3.25rem, 9.4vw, 7.1rem); } -.nav-link:hover, -.nav-link:focus-visible { - --nav-link-active: 1; - --nav-link-play-state: running; - text-shadow: 0 0 12px rgba(255, 255, 255, 0.28); + +.page-hero__title { + font-size: clamp(2.85rem, 7vw, 5.25rem); } -.nav-link:active { - transform: translateY(1px); + +.section-intro__title, +.contact-banner__title { + font-size: clamp(2.4rem, 5vw, 4.2rem); } -.nav-link:focus-visible { - outline: none; - box-shadow: 0 0 0 3px rgba(251, 146, 60, 0.15); + +.home-hero__title span, +.page-hero__title span, +.section-intro__title span, +.contact-banner__title span { + color: var(--ww-orange); + font-style: italic; } -.nav-link__label { - position: relative; - z-index: 3; + +.home-hero__description, +.page-hero__description, +.section-intro__description, +.contact-banner__description { + max-width: 42rem; + margin: 0; + color: var(--ww-muted-strong); + font-size: clamp(1rem, 1.7vw, 1.16rem); + line-height: 1.72; } -.nav-link__backdrop { - position: absolute; - inset: 0; - border-radius: inherit; - background: transparent; - opacity: 0; - pointer-events: none; - z-index: 1; + +.home-hero__actions, +.page-hero__actions, +.contact-banner__actions, +.detail-card__actions { + display: flex; + flex-wrap: wrap; + gap: 0.9rem; } -.nav-link__spark { - position: absolute; - inset: 0; - border-radius: inherit; - pointer-events: none; - z-index: 2; - box-sizing: border-box; - padding: var(--nav-link-stroke); - background: conic-gradient( - from calc(var(--nav-link-angle) - 90deg), - transparent 0deg, - transparent 332deg, - var(--nav-link-spark-color, rgba(255, 255, 255, 0.95)) 360deg - ); - mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0); - mask-mode: alpha; - mask-repeat: no-repeat; - mask-origin: content-box, border-box; - mask-clip: content-box, border-box; - mask-composite: exclude; - -webkit-mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0); - -webkit-mask-repeat: no-repeat; - -webkit-mask-origin: content-box, border-box; - -webkit-mask-clip: content-box, border-box; - -webkit-mask-composite: xor; - opacity: var(--nav-link-active); - filter: drop-shadow( - 0 0 calc(var(--nav-link-active) * 16px) - var(--nav-link-glow-color, rgba(255, 255, 255, 0.45)) - ); - transition: - opacity var(--nav-link-transition), - filter var(--nav-link-transition); - animation: nav-link-spin var(--nav-link-spin) linear infinite; - animation-play-state: var(--nav-link-play-state); + +.home-hero__actions, +.page-hero__actions { + margin-top: 2rem; } -.nav-link:hover .nav-link__spark, -.nav-link:focus-visible .nav-link__spark { - opacity: 1; + +.home-hero__media, +.page-hero__photo { + position: relative; } -.nav-link--bordered { - border: 1px solid rgba(148, 163, 184, 0.28); +.home-hero__image-frame, +.page-hero__photo, +.media-panel { + position: relative; + overflow: hidden; + border-radius: 1.5rem; + border: 1px solid var(--ww-line-strong); + box-shadow: var(--ww-shadow); + background: var(--ww-surface-strong); } -.nav-link--bordered:hover, -.nav-link--bordered:focus-visible { - border-color: rgba(148, 163, 184, 0.42); + +.home-hero__image-frame { + min-height: 34rem; } -@property --nav-link-angle { - syntax: ""; - initial-value: 0deg; - inherits: false; +.page-hero__photo { + min-height: 22rem; } -@keyframes rotate-spark { - from { - rotate: 0deg; - } - to { - rotate: 360deg; - } +.home-hero__image, +.media-panel__image, +.photo-collage__image { + object-fit: cover; } -@keyframes nav-link-spin { - from { - --nav-link-angle: 0deg; - } - to { - --nav-link-angle: 360deg; - } +.home-hero__floating-card { + position: absolute; + left: -1.5rem; + bottom: 1.5rem; + max-width: 18rem; + padding: 1.1rem 1.2rem; + color: var(--ww-navy-deep); + background: var(--ww-off-white); + border-left: 4px solid var(--ww-orange); + box-shadow: 0 18px 36px rgba(0, 0, 0, 0.24); +} + +.home-hero__floating-card p, +.page-note p, +.tier-block__heading p, +.detail-card__eyebrow, +.publication-year-block__header span, +.site-footer__label, +.site-footer__kicker, +.metric-strip__item span, +.result-card__date { + margin: 0; + font-family: var(--font-mono); + font-size: 0.74rem; + text-transform: uppercase; + letter-spacing: 0.12em; } -@keyframes flip { - to { - rotate: 360deg; - } +.home-hero__floating-card p, +.page-note p, +.detail-card__eyebrow { + color: rgba(8, 23, 47, 0.54); } -/* Header CTA (white pill) */ -.nav-link--cta { - margin-left: 0.75rem; - padding: 0.55rem 1.35rem; - font-weight: 700; - letter-spacing: 0.06em; - text-transform: uppercase; - color: #0b1020; - --nav-link-stroke: 0.24rem; - --nav-link-spark-color: rgba(88, 137, 255, 0.95); - --nav-link-glow-color: rgba(88, 137, 255, 0.45); +.home-hero__floating-card h2, +.page-note h2, +.detail-card h2 { + margin: 0.45rem 0 0; + font-family: var(--font-display); + font-size: 1.5rem; + line-height: 1.02; + letter-spacing: -0.03em; } -.nav-link--cta .nav-link__label { - font-size: 0.92rem; - color: inherit; + +.home-hero__floating-card span, +.page-note span { + display: block; + margin-top: 0.4rem; + color: rgba(8, 23, 47, 0.7); + line-height: 1.55; } -.nav-link--cta, -.nav-link--cta:hover, -.nav-link--cta:focus-visible { - text-shadow: none; + +.metric-strip { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + margin-top: 2rem; + border-block: 1px solid var(--ww-line); } -.nav-link--cta .nav-link__backdrop { - background: #ffffff; - opacity: 1; + +.metric-strip__item { + padding: 1.5rem 1.1rem; } -.nav-link--cta:hover .nav-link__backdrop, -.nav-link--cta:focus-visible .nav-link__backdrop { - background: #ffffff; - opacity: 1; + +.metric-strip__item + .metric-strip__item { + border-left: 1px solid var(--ww-line); } -.nav-link--cta-mobile { - display: none; +.metric-strip__item p { + margin: 0 0 0.4rem; + font-family: var(--font-display); + font-size: clamp(2rem, 4vw, 3.6rem); + line-height: 0.95; } -.nav-link--cta-desktop { - display: inline-flex; +.metric-strip__item span { + color: var(--ww-muted); } -/* Utility */ -.grid { - display: grid; - gap: 1rem; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +.sponsor-rail { + border-top: 1px solid var(--ww-line); + border-bottom: 1px solid var(--ww-line); } -.row { + +.sponsor-rail__inner { display: flex; - gap: 1rem; - flex-wrap: wrap; + align-items: center; + gap: 1.4rem 2rem; + padding: 1.35rem 0; } -.panel { - background: linear-gradient( - 180deg, - rgba(27, 39, 66, 0.6), - rgba(27, 39, 66, 0.35) - ); - border: 1px solid rgba(96, 165, 250, 0.16); - border-radius: 1rem; - padding: 1.1rem; - box-shadow: var(--shadow); +.sponsor-rail__inner > p { + flex-shrink: 0; + margin: 0; + font-family: var(--font-mono); + font-size: 0.76rem; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--ww-muted); +} + +.sponsor-rail__logos { + display: flex; + flex-wrap: wrap; + gap: 0.85rem; + align-items: center; } -.btn { +.sponsor-rail__logo-card { display: inline-flex; align-items: center; - gap: 0.45rem; - color: var(--ink); - text-decoration: none; - font-weight: 600; - border: 1px solid rgba(96, 165, 250, 0.28); - border-radius: 9999px; - padding: 0.42rem 0.75rem; - background: linear-gradient( - 180deg, - rgba(96, 165, 250, 0.08), - rgba(96, 165, 250, 0.03) - ); - box-shadow: none; - backdrop-filter: saturate(120%) blur(4px); - transition: - border-color 0.2s ease, - background 0.2s ease, - transform 0.1s ease; -} -.btn:hover { - border-color: rgba(251, 146, 60, 0.45); - background: linear-gradient( - 180deg, - rgba(251, 146, 60, 0.12), - rgba(251, 146, 60, 0.04) - ); -} -.btn:active { - transform: translateY(1px); -} -.btn:focus { - outline: none; - box-shadow: 0 0 0 3px rgba(251, 146, 60, 0.15); -} - -.btn.sec { - border: 1px solid rgba(96, 165, 250, 0.28); - background: linear-gradient( - 180deg, - rgba(18, 26, 46, 0.65), - rgba(18, 26, 46, 0.45) - ); -} -.btn.sec:hover { - border-color: rgba(251, 146, 60, 0.5); - background: linear-gradient( - 180deg, - rgba(251, 146, 60, 0.16), - rgba(251, 146, 60, 0.05) - ); -} - -.badge { - display: inline-block; - font-size: 0.8rem; - color: #0b1020; - background: linear-gradient(90deg, var(--orange-400), var(--orange-500)); - padding: 0.2rem 0.5rem; - border-radius: 0.5rem; -} - -.card { - background: linear-gradient( - 180deg, - rgba(18, 26, 46, 0.8), - rgba(18, 26, 46, 0.55) - ); - border: 1px solid rgba(96, 165, 250, 0.16); + justify-content: center; + min-height: 4.5rem; + padding: 0.85rem 1.15rem; + border: 1px solid var(--ww-line); border-radius: 1rem; - padding: 1.1rem; - box-shadow: var(--shadow); -} -.card h3 { - margin: 0.2rem 0 0.4rem; - font-size: 1.1rem; + background: rgba(245, 242, 236, 0.03); + transition: + transform 0.18s ease, + border-color 0.18s ease, + background-color 0.18s ease; } -.card p { - margin: 0.3rem 0; - color: var(--ink-muted); + +.sponsor-rail__logo-card:hover, +.sponsor-rail__logo-card:focus-visible { + border-color: rgba(243, 154, 106, 0.5); + background: rgba(245, 242, 236, 0.06); + transform: translateY(-1px); } -.card .meta { - font-size: 0.9rem; - color: var(--ink-dim); + +.site-section { + padding: clamp(4rem, 8vw, 6.5rem) 0; } -.card .actions { - display: flex; - gap: 0.6rem; - margin-top: 0.7rem; + +.site-section--deep { + background: rgba(4, 13, 28, 0.26); + border-block: 1px solid var(--ww-line); } -/* Forms */ -.form { - display: grid; - gap: 1rem; - max-width: 720px; +.site-section--tight-top { + padding-top: 1rem; } -.field { - display: grid; - gap: 0.4rem; + +.section-intro { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 1rem 2rem; + margin-bottom: 2.4rem; } -.field label { - color: var(--ink); - font-weight: 600; + +.section-intro--compact { + display: block; + margin-bottom: 0; } -.input, -.textarea { - width: 100%; - color: var(--ink); - background: rgba(18, 26, 46, 0.6); - border: 1px solid rgba(96, 165, 250, 0.2); - border-radius: 0.75rem; - padding: 0.7rem 0.8rem; + +.section-intro__copy { + max-width: 42rem; } -.textarea { - min-height: 140px; - resize: vertical; + +.section-intro__eyebrow { + display: inline-block; + color: var(--ww-orange-soft); + font-family: var(--font-mono); + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.13em; } -.input:focus, -.textarea:focus { - outline: none; - border-color: rgba(251, 146, 60, 0.5); - box-shadow: 0 0 0 3px rgba(251, 146, 60, 0.15); + +.section-intro__description { + max-width: 38rem; } -.hero-container { +.result-timeline { display: flex; flex-direction: column; - align-items: center; - text-align: center; - z-index: 2; - animation: fadeInUp 1s ease-out; + border-top: 1px solid var(--ww-line); } -.headline { - margin: 0; - font-size: clamp(2.25rem, 6vw, 4rem); - line-height: 1.18; - padding-bottom: 0.05em; - background: linear-gradient( - 90deg, - var(--grad-a), - var(--grad-b), - var(--grad-c) - ); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - animation: shimmer 3.2s ease-in-out infinite alternate; - text-shadow: 0 0 22px rgba(67, 97, 238, 0.18); -} - -.headline-row { - display: flex; +.result-card { + display: grid; + grid-template-columns: 6rem minmax(0, 1fr) 13rem 10rem; + gap: 1.4rem; align-items: center; - justify-content: center; - gap: 0.6rem; - margin-bottom: 0.75rem; - width: max-content; - margin-left: auto; - margin-right: auto; + padding: 1.45rem 0; + border-bottom: 1px solid var(--ww-line); } -.logo-hero { - height: 8em; - width: auto; - display: block; - filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.35)); - margin-bottom: 1rem; - padding-right: 1em; +.result-card--highlight { + background: linear-gradient(90deg, rgba(232, 101, 43, 0.08) 0%, transparent 60%); } -.irl-text { - font-family: var(--font-headline); - background: none !important; - -webkit-background-clip: unset !important; - background-clip: unset !important; - color: #f26722 !important; +.result-card__date { + color: var(--ww-muted); } -.team { - margin: 0.2rem 0 0 0; - color: var(--ink); - font-size: clamp(1.1rem, 2.2vw, 1.4rem); - text-align: center; - font-weight: 500; - opacity: 0; - animation: fadeIn 1s ease-out 0.5s forwards; +.result-card__main h3, +.pillar-card h3, +.publication-card__title, +.social-card h3, +.tier-block__heading p, +.site-footer__summary, +.detail-card h2, +.topic-list__item h3 { + margin: 0; } -.tagline { - margin: 0.3rem 0 2rem 0; - color: var(--ink-dim); - font-size: clamp(0.95rem, 1.8vw, 1.1rem); - text-align: center; - opacity: 0; - animation: fadeIn 1s ease-out 0.7s forwards; +.result-card__main h3 { + font-size: 1.35rem; + color: var(--ww-off-white); } -/* Animations */ -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } + +.result-card__main p, +.pillar-card > p:last-child, +.social-card__description, +.tier-block__heading span, +.detail-card p, +.topic-list__item p, +.site-footer__summary { + margin: 0.4rem 0 0; + color: var(--ww-muted-strong); + line-height: 1.6; } -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } +.result-card__location { + display: flex; + align-items: center; + gap: 0.45rem; + margin: 0; + color: var(--ww-muted); } -@keyframes float { - 0%, - 100% { - transform: translateY(0px); - } - 50% { - transform: translateY(-10px); - } +.result-card__result { + margin: 0; + color: var(--ww-orange-soft); + font-family: var(--font-display); + font-size: 1.5rem; + text-align: right; } -@keyframes shimmer { - 0% { - filter: brightness(1) saturate(1); - } - 100% { - filter: brightness(1.25) saturate(1.35); - } +.team-showcase, +.socials-showcase, +.contact-layout { + display: grid; + gap: 1.5rem 2rem; } -@keyframes windWhoosh { - 0% { - transform: translateX(0) skewX(0deg); - } - 30% { - transform: translateX(2px) skewX(-1deg); - } - 60% { - transform: translateX(-1px) skewX(0.5deg); - } - 100% { - transform: translateX(0) skewX(0deg); - } +.team-showcase { + grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); + align-items: start; } -/* (removed unused keyframes for spinner/glow/spheres) */ +.team-showcase__intro { + position: sticky; + top: 6.7rem; + align-self: start; +} -.hero-like { - padding-top: 8rem; - padding-bottom: 8rem; +.team-showcase__photos { + display: grid; + grid-template-columns: 1.25fr 0.75fr; + gap: 1rem; + margin-top: 2rem; } -.hero-backdrop { - position: absolute; - inset: 0; - background: transparent; - pointer-events: none; +.media-panel { + min-height: 18rem; } -.asterisk { - /* For image logo on hero */ - width: clamp(48px, 8vw, 80px); - height: auto; - display: block; - opacity: 0.95; - margin-bottom: 1.5rem; - filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.35)); -} - -.headline.plain { - background: none; - font-family: var(--font-headline); - -webkit-background-clip: unset; - background-clip: unset; - color: #ffffff; - text-shadow: none; - animation: none; - font-size: clamp(2.6rem, 6.8vw, 4.6rem); - line-height: 1.1; - letter-spacing: 0.2px; -} - -.btn.hero { - color: #0b1020; - background: #ffffff; - border-color: rgba(255, 255, 255, 0.85); - box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28); - font-weight: 700; - padding: 0.75rem 1.1rem; +.media-panel--tall { + min-height: 24rem; } -.btn.hero:hover { - background: #ffffff; - transform: translateY(-1px); - box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35); + +.pillar-grid, +.publication-preview-grid, +.social-grid, +.publication-year-block__grid { + display: grid; + gap: 1rem; } -.btn-icon { - margin-right: 0.25rem; + +.pillar-grid, +.publication-preview-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); } -/* --- Wind Particles --- */ -.wind-particles { - position: absolute; - inset: 0; - pointer-events: none; - z-index: 1; - overflow: hidden; + +.pillar-card { + padding: 1.5rem; + border: 1px solid var(--ww-line); + border-radius: 1.25rem; + background: rgba(245, 242, 236, 0.03); } -.wind-particle { - position: absolute; - background: rgba(255, 255, 255, 0.4); - border-radius: 1px; -} - -/* Individual particle styles and animations using SCSS loop */ -@for $i from 1 through 40 { - .wind-particle:nth-child(#{$i}) { - // Store random vertical position to use in both keyframes - $ypos: math.random(100); - // Random duration between 8-24 seconds - $duration: 8 + math.random(17); - - // Random width between 3-9px - width: (3 + math.random(7)) + px; - // Random height between 1-2px - height: (1 + math.random(2)) + px; - // Random opacity between 0.15-0.5 - opacity: (0.15 + math.div(math.random(35), 100)); - animation: drift#{$i} $duration + s linear infinite; - // Negative delay to spread particles across screen (0 to -duration range) - animation-delay: (-1 * math.random($duration)) + s; - - // Define keyframes for this particle - @keyframes drift#{$i} { - 0% { - left: -10%; - // Use same vertical position throughout - top: $ypos + 1%; - } - 100% { - left: 110%; - // Keep vertical position constant - top: $ypos + 1%; - } - } - } +.pillar-card > p:first-child { + margin: 0 0 0.7rem; + color: var(--ww-orange-soft); + font-family: var(--font-mono); + letter-spacing: 0.12em; } -/* --- Publications list --- */ -.year-group { - padding-bottom: 2rem; - border-bottom: 1px solid rgba(96, 165, 250, 0.15); +.publication-card, +.social-card, +.detail-card { + border: 1px solid var(--ww-line); + border-radius: 1.3rem; + background: var(--ww-surface); } -.year-group:last-child { - border-bottom: none; + +.publication-card { + display: grid; + grid-template-columns: 7rem minmax(0, 1fr); + gap: 1rem; + padding: 1.3rem; } -.sponsor-entry { - padding-bottom: 2rem; - border-bottom: 1px solid rgba(96, 165, 250, 0.15); +.publication-card__meta { + display: flex; + flex-direction: column; + gap: 0.3rem; + color: var(--ww-muted); + font-family: var(--font-mono); + font-size: 0.73rem; + text-transform: uppercase; + letter-spacing: 0.11em; } -.sponsor-entry:last-child { - border-bottom: none; - padding-bottom: 0; +.publication-card__body { + min-width: 0; } -/* --- Socials --- */ -.social-collage { - display: grid; - grid-auto-flow: dense; - grid-auto-rows: 84px; - grid-template-columns: repeat(12, minmax(0, 1fr)); - gap: 0.75rem; - margin: 3rem 0 0; +.publication-card__title { + display: inline-block; + color: var(--ww-off-white); + font-size: 1.16rem; + font-weight: 600; + line-height: 1.35; + text-decoration: none; } -.social-collage__item { - position: relative; - border-radius: 1rem; - overflow: hidden; - box-shadow: 0 16px 28px rgba(11, 16, 32, 0.18); + +.publication-card__title:hover, +.publication-card__title:focus-visible { + color: var(--ww-orange-soft); } -.social-collage__item::after { - content: ""; - position: absolute; - inset: 0; - background: linear-gradient(145deg, rgba(8, 12, 26, 0.18), transparent 55%); - opacity: 0; - transition: opacity 0.3s ease; - pointer-events: none; + +.publication-card__authors { + margin: 0.55rem 0 0; + color: var(--ww-muted-strong); + line-height: 1.6; } -.social-collage__item:hover::after, -.social-collage__item:focus-within::after { - opacity: 1; + +.publication-card__tags { + display: flex; + flex-wrap: wrap; + gap: 0.55rem; + margin-top: 0.9rem; } -.social-collage__image { - object-fit: cover; + +.publication-card__tags span { + display: inline-flex; + align-items: center; + min-height: 2rem; + padding: 0.25rem 0.75rem; + border: 1px solid rgba(243, 154, 106, 0.22); + border-radius: 999px; + color: var(--ww-muted-strong); + background: rgba(232, 101, 43, 0.08); + font-size: 0.84rem; +} + +.publication-card__actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-top: 1rem; +} + +.utility-button { + display: inline-flex; + align-items: center; + gap: 0.45rem; + min-height: 2.35rem; + padding: 0.45rem 0.8rem; + color: var(--ww-muted-strong); + border: 1px solid var(--ww-line-strong); + border-radius: 999px; + background: transparent; + cursor: pointer; transition: - transform 0.45s ease, - filter 0.45s ease; + transform 0.18s ease, + border-color 0.18s ease, + background-color 0.18s ease, + color 0.18s ease; } -.social-collage__item:hover .social-collage__image, -.social-collage__item:focus-within .social-collage__image { - transform: scale(1.05); - filter: saturate(1.1); + +.utility-button:hover { + border-color: rgba(243, 154, 106, 0.55); + color: var(--ww-off-white); + background: rgba(245, 242, 236, 0.05); } -.social-collage__item--wide { - grid-column: span 6; - grid-row: span 4; + +.utility-button--success { + border-color: rgba(84, 192, 126, 0.38); + background: rgba(84, 192, 126, 0.12); + color: #c7f1d7; } -.social-collage__item--tall { - grid-column: span 3; - grid-row: span 4; + +.sponsor-tier-grid { + display: grid; + gap: 1.25rem; } -.social-collage__item--square { - grid-column: span 3; - grid-row: span 4; + +.tier-block { + display: grid; + grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); + gap: 1.25rem; + padding: 1.5rem; + border: 1px solid var(--ww-line); + border-radius: 1.5rem; + background: rgba(245, 242, 236, 0.025); } -.social-collage__item--panorama { - grid-column: span 6; - grid-row: span 4; + +.tier-block__heading { + max-width: 16rem; } -@media (max-width: 960px) { - .social-collage { - grid-auto-rows: 96px; - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - .social-collage__item--wide, - .social-collage__item--panorama { - grid-column: span 8; - } - .social-collage__item--tall, - .social-collage__item--square { - grid-column: span 4; - } +.tier-block__heading p { + color: var(--ww-orange-soft); } -@media (max-width: 640px) { - .social-collage { - grid-auto-rows: 160px; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0.6rem; - margin-top: 2.4rem; - } - .social-collage__item { - box-shadow: 0 12px 22px rgba(11, 16, 32, 0.18); - } - .social-collage__item--wide, - .social-collage__item--panorama, - .social-collage__item--tall, - .social-collage__item--square { - grid-column: span 2; - grid-row: span 2; - } +.tier-block__heading span { + display: block; } -.social-grid { +.tier-block__cards { display: grid; - gap: 1.2rem; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1rem; } -.social-card { + +.sponsor-card { display: flex; flex-direction: column; - gap: 0.85rem; + gap: 1rem; min-height: 100%; - padding: 1.25rem 1.3rem; - border-radius: 1rem; - border: 1px solid rgba(96, 165, 250, 0.16); - background: none; - box-shadow: none; - transition: - transform 0.2s ease, - box-shadow 0.2s ease, - border-color 0.2s ease; - text-decoration: none; - color: inherit; + padding: 1.25rem; + border: 1px solid var(--ww-line); + border-radius: 1.2rem; + background: rgba(245, 242, 236, 0.03); } -.social-card:hover, -.social-card:focus-visible { - transform: translateY(-2px); - box-shadow: 0 16px 32px rgba(6, 10, 20, 0.28); - border-color: rgba(251, 146, 60, 0.35); + +.sponsor-card__media { + display: flex; + flex-direction: column; + gap: 1rem; } -.social-card__header { + +.sponsor-card__logo { display: flex; align-items: center; - gap: 0.75rem; + justify-content: center; + min-height: 7.5rem; + padding: 1rem; + border-radius: 1rem; + background: var(--ww-off-white); } -.social-card__logo { - width: 42px; - height: 42px; - flex-shrink: 0; + +.sponsor-card__meta h3 { + font-size: 1.12rem; +} + +.sponsor-card__link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + margin-top: 0.55rem; + color: var(--ww-orange-soft); + text-decoration: none; +} + +.socials-showcase { + grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); + align-items: start; +} + +.social-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); } -.social-card__heading { + +.social-card { display: flex; flex-direction: column; - gap: 0.25rem; -} -.social-card__name { - margin: 0; - font-size: 1.15rem; - color: var(--ink); + justify-content: space-between; + gap: 1rem; + min-height: 14.5rem; + padding: 1.3rem; + text-decoration: none; + transition: + transform 0.18s ease, + border-color 0.18s ease, + background-color 0.18s ease; } -.social-card__handle { - margin: 0; - font-size: 0.95rem; - color: var(--ink-dim); + +.social-card:hover { + border-color: rgba(243, 154, 106, 0.4); + background: rgba(245, 242, 236, 0.05); } -.social-card__description { - margin: 0; - font-size: 0.95rem; - color: var(--ink-muted); + +.social-card__header { + display: flex; + align-items: center; + gap: 0.9rem; } -.contact-header { - display: grid; - gap: 0.75rem; - max-width: 720px; - margin-bottom: 2.5rem; +.social-card__header h3 { + font-size: 1.18rem; } -.contact-layout { - display: grid; - gap: 1.6rem; + +.social-card__header p { + margin: 0.2rem 0 0; + color: var(--ww-muted); } -.contact-main { - display: grid; - gap: 1.4rem; + +.social-card__cta { + display: inline-flex; + align-items: center; + gap: 0.45rem; + color: var(--ww-orange-soft); + font-weight: 600; } -.contact-sidebar { + +.photo-collage { display: grid; - gap: 1.2rem; + grid-template-columns: repeat(12, minmax(0, 1fr)); + grid-auto-rows: 4.8rem; + gap: 0.8rem; } -@media (min-width: 960px) { - .contact-layout { - grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); - align-items: start; - } - .contact-sidebar { - position: sticky; - top: 7.5rem; - } -} -.contact-highlight { +.photo-collage__item { position: relative; - display: grid; - gap: 1rem; - padding: 1.8rem 2rem; - border-radius: 1.3rem; - border: 1px solid rgba(96, 165, 250, 0.18); - background: linear-gradient( - 150deg, - rgba(36, 54, 94, 0.92), - rgba(18, 26, 46, 0.78) - ); - box-shadow: var(--shadow); overflow: hidden; + border-radius: 1.15rem; + border: 1px solid var(--ww-line); + background: rgba(245, 242, 236, 0.05); } -.contact-highlight::after { - content: ""; - position: absolute; - inset: -30% 60% -60% -10%; - width: clamp(220px, 32vw, 360px); - height: 220%; - background: radial-gradient( - circle at center, - rgba(96, 165, 250, 0.15), - transparent 65% - ); - pointer-events: none; - opacity: 0.8; - z-index: 0; + +.photo-collage__item--wide { + grid-column: span 4; + grid-row: span 3; } -.contact-highlight > * { - position: relative; - z-index: 1; + +.photo-collage__item--square { + grid-column: span 3; + grid-row: span 3; } -.contact-highlight__title { - margin: 0; - font-size: clamp(1.3rem, 2.4vw, 1.75rem); + +.photo-collage__item--tall { + grid-column: span 3; + grid-row: span 5; } -.contact-highlight__copy { - margin: 0; - color: var(--ink-muted); - max-width: 48ch; + +.photo-collage__item--panorama { + grid-column: span 6; + grid-row: span 3; } -.contact-highlight__actions { + +.contact-banner { display: flex; - flex-wrap: wrap; - gap: 0.75rem; - z-index: 1; + align-items: center; + justify-content: space-between; + gap: 2rem; + padding: 2rem; + border: 1px solid var(--ww-line); + border-radius: 1.65rem; + background: + radial-gradient(circle at right, rgba(232, 101, 43, 0.16), transparent 32%), + rgba(245, 242, 236, 0.03); +} + +.page-hero__metrics { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1rem; + margin: 2rem 0 0; } -.contact-eyebrow { + +.page-hero__metric { + padding-top: 1rem; + border-top: 1px solid var(--ww-line); +} + +.page-hero__metric dt { + margin: 0 0 0.45rem; + color: var(--ww-muted); + font-family: var(--font-mono); font-size: 0.75rem; text-transform: uppercase; - letter-spacing: 0.14em; - color: var(--ink-dim); + letter-spacing: 0.11em; +} + +.page-hero__metric dd { margin: 0; - z-index: 1; + font-family: var(--font-display); + font-size: 1.55rem; } -.contact-card { - display: grid; - gap: 0.9rem; +.page-note, +.detail-card { + padding: 1.5rem; +} + +.page-note { + height: 100%; + color: var(--ww-navy-deep); + border-radius: 1.5rem; + background: var(--ww-off-white); + box-shadow: var(--ww-shadow); } -.contact-details { + +.news-feed { display: grid; - gap: 0.7rem; + gap: 1.75rem; +} + +.news-feed__empty { margin: 0; + padding: 2rem; + color: var(--ww-muted-strong); + text-align: center; + border: 1px dashed var(--ww-line); + border-radius: 1.3rem; + background: var(--ww-surface); } -.contact-detail { - display: grid; - gap: 0.25rem; + +.news-post { + padding: 1.75rem clamp(1.25rem, 3vw, 2.25rem); + border: 1px solid var(--ww-line); + border-radius: 1.3rem; + background: var(--ww-surface); } -.contact-detail dt { - font-size: 0.8rem; + +.news-post__meta { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 1.1rem; + color: var(--ww-muted); + font-family: var(--font-mono); + font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.12em; - color: var(--ink-dim); } -.contact-detail dd { + +.news-post__title { + margin: 0.65rem 0 0.5rem; + color: var(--ww-off-white); + font-family: var(--font-display); + font-size: clamp(1.65rem, 3vw, 2.25rem); + line-height: 1.08; + letter-spacing: -0.02em; +} + +.news-post__summary { margin: 0; - font-size: 1rem; + color: var(--ww-muted-strong); + font-size: 1.05rem; + line-height: 1.6; } -.contact-link { - color: inherit; - text-decoration: none; - border-bottom: 1px solid rgba(96, 165, 250, 0.35); - padding-bottom: 0.1rem; - transition: border-color 0.2s ease; + +.news-post__cover { + position: relative; + margin: 1.4rem 0 0.4rem; + overflow: hidden; + border-radius: 1rem; + border: 1px solid var(--ww-line); + background: var(--ww-surface-strong); } -.contact-link:hover, -.contact-link:focus-visible { - border-color: rgba(251, 146, 60, 0.55); + +.news-post__cover img { + display: block; + width: 100%; + height: auto; } -.contact-meta { + +.news-post__body { + margin-top: 1.25rem; + color: var(--ww-cream); + line-height: 1.7; +} + +.news-post__body > * { margin: 0; - font-size: 0.9rem; - color: var(--ink-muted); } -.contact-address { - font-style: normal; - line-height: 1.6; - color: var(--ink-dim); + +.news-post__body > * + * { + margin-top: 1.5em; +} + +.news-post__body h1, +.news-post__body h2, +.news-post__body h3, +.news-post__body h4 { + margin: 1.6rem 0 0.6rem; + color: var(--ww-off-white); + font-family: var(--font-display); + line-height: 1.2; + letter-spacing: -0.01em; +} + +.news-post__body h2 { + font-size: 1.4rem; +} + +.news-post__body h3 { + font-size: 1.18rem; +} + +.news-post__body a { + color: var(--ww-orange-soft); + text-decoration: underline; + text-decoration-color: rgba(243, 154, 106, 0.4); + text-underline-offset: 0.18em; +} + +.news-post__body a:hover { + color: var(--ww-off-white); + text-decoration-color: currentColor; +} + +.news-post__body ul, +.news-post__body ol { margin: 0; + padding-left: 1.4rem; } -.contact-card--topics { - gap: 1.2rem; + +.news-post__body li + li { + margin-top: 0.35rem; } -.contact-topics { - display: grid; - gap: 1.1rem; + +.news-post__body code { + padding: 0.12rem 0.4rem; + border-radius: 0.35rem; + background: rgba(245, 242, 236, 0.08); + font-family: var(--font-mono); + font-size: 0.9em; } -.contact-topics h4 { - margin: 0 0 0.4rem; - font-size: 1.05rem; + +.news-post__body pre { + margin: 0; + padding: 1rem 1.1rem; + overflow-x: auto; + border: 1px solid var(--ww-line); + border-radius: 0.85rem; + background: rgba(4, 13, 28, 0.6); + font-family: var(--font-mono); + font-size: 0.88rem; + line-height: 1.55; +} + +.news-post__body pre code { + padding: 0; + background: transparent; } -.contact-topics p { + +.news-post__body blockquote { margin: 0; - color: var(--ink-muted); + padding: 0.6rem 1rem; + border-left: 3px solid var(--ww-orange); + color: var(--ww-muted-strong); + background: rgba(232, 101, 43, 0.06); + border-radius: 0 0.6rem 0.6rem 0; } -@media (min-width: 720px) { - .contact-topics { - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 1.2rem; - } +.news-post__body img { + display: block; + margin: 0; + max-width: 100%; + height: auto; + border-radius: 0.85rem; +} + +.news-post__body hr { + margin: 1.6rem 0; + border: 0; + border-top: 1px solid var(--ww-line); } -.contact-actions { + +.news-post__body table { + width: 100%; + border-collapse: collapse; + font-size: 0.95rem; +} + +.news-post__body th, +.news-post__body td { + padding: 0.55rem 0.7rem; + border-bottom: 1px solid var(--ww-line); + text-align: left; +} + +.news-post__tags { display: flex; flex-wrap: wrap; - gap: 0.8rem; + gap: 0.55rem; + margin-top: 1.4rem; +} + +.news-post__tags span { + display: inline-flex; + align-items: center; + min-height: 2rem; + padding: 0.25rem 0.75rem; + border: 1px solid rgba(243, 154, 106, 0.22); + border-radius: 999px; + color: var(--ww-muted-strong); + background: rgba(232, 101, 43, 0.08); + font-size: 0.84rem; +} + +.publication-year-list { + display: grid; + gap: 1.5rem; +} + +.publication-year-block { + display: grid; + grid-template-columns: 9rem minmax(0, 1fr); + gap: 1.5rem; + padding-top: 1.4rem; + border-top: 1px solid var(--ww-line); +} + +.publication-year-block__header p { + margin: 0; + font-family: var(--font-display); + font-size: 2.5rem; + line-height: 0.9; +} + +.publication-year-block__header span { + display: inline-block; + margin-top: 0.55rem; + color: var(--ww-muted); +} + +.contact-layout { + grid-template-columns: minmax(0, 1.05fr) minmax(280px, 0.95fr); + align-items: start; } -.pub-grid { +.contact-stack { display: grid; gap: 1rem; - grid-template-columns: 1fr; } -.pub-item { - display: flex; - align-items: center; + +.detail-card__link { + display: inline-flex; + margin-top: 0.7rem; + color: var(--ww-orange-soft); + font-family: var(--font-display); + font-size: 1.5rem; + text-decoration: none; +} + +.detail-card__address { + display: grid; + gap: 0.2rem; + margin-top: 0.9rem; + color: var(--ww-muted-strong); + font-style: normal; +} + +.topic-list { + display: grid; gap: 1rem; - padding: 1rem 1.2rem; - background: linear-gradient( - 180deg, - rgba(27, 39, 66, 0.55), - rgba(27, 39, 66, 0.35) - ); - border: 1px solid rgba(96, 165, 250, 0.16); - border-radius: 1rem; - box-shadow: var(--shadow); - transition: - border-color 0.2s ease, - transform 0.1s ease, - background 0.2s ease; } -.pub-item:hover { - border-color: rgba(251, 146, 60, 0.35); - transform: translateY(-1px); - background: linear-gradient( - 180deg, - rgba(27, 39, 66, 0.6), - rgba(27, 39, 66, 0.4) - ); + +.topic-list__item + .topic-list__item { + padding-top: 1rem; + border-top: 1px solid var(--ww-line); } -.pub-body { - flex: 1 1 auto; - min-width: 0; +.topic-list__item h3 { + font-size: 1.08rem; +} + +.site-footer { + padding: 0 0 calc(2rem + env(safe-area-inset-bottom, 0px)); + border-top: 1px solid var(--ww-line); + background: rgba(5, 15, 31, 0.38); } -.pub-title { - margin: 0.1rem 0; - font-size: 1.25rem; + +.site-footer__grid { + display: grid; + grid-template-columns: minmax(0, 1.3fr) repeat(2, minmax(0, 0.7fr)); + gap: 1.5rem 2rem; + padding: 2rem 0; } -.pub-sub { - margin: 0.1rem 0; - color: var(--ink); - opacity: 0.9; + +.site-footer__kicker, +.site-footer__label { + color: var(--ww-orange-soft); } -.pub-meta { - margin: 0.1rem 0; - color: var(--ink-muted); + +.site-footer__summary { + max-width: 30rem; } -.pub-actions { - margin-left: 0.5rem; +.site-footer__column { + display: flex; + flex-direction: column; + gap: 0.55rem; } -.pub-link { - color: var(--ink); + +.site-footer__column a, +.site-footer__column p { + margin: 0; + color: var(--ww-muted-strong); + line-height: 1.55; text-decoration: none; - padding: 0.35rem 0.6rem; - border: 1px solid rgba(96, 165, 250, 0.28); - border-radius: 9999px; - background: linear-gradient( - 180deg, - rgba(96, 165, 250, 0.08), - rgba(96, 165, 250, 0.03) - ); -} -.pub-link:hover { - border-color: rgba(251, 146, 60, 0.45); -} - -@media (max-width: 960px) { - body.mobile-nav-open { - overflow: hidden; - } +} - .site-header { - padding-bottom: 1.5rem; +.site-footer__column a:hover, +.site-footer__column a:focus-visible { + color: var(--ww-off-white); +} + +@media (max-width: 1040px) { + .home-hero__grid, + .page-hero__grid, + .team-showcase, + .socials-showcase, + .contact-layout, + .tier-block, + .site-footer__grid { + grid-template-columns: 1fr; } - .site-header:not(.site-header--open) { - mask-image: linear-gradient( - to bottom, - black 0%, - black 62%, - transparent 100% - ); - -webkit-mask-image: linear-gradient( - to bottom, - black 0%, - black 62%, - transparent 100% - ); + .team-showcase__intro { + position: static; } - .site-header.site-header--open { - mask-image: none; - -webkit-mask-image: none; + .social-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); } - .nav { - position: relative; - gap: 0.75rem; - padding-block: 1rem; + .page-hero__aside { + max-width: 30rem; } +} +@media (max-width: 900px) { .nav-toggle { display: inline-flex; } - .nav-list-container { - position: absolute; - inset: calc(100% + 0.75rem) 2rem auto 2rem; + .nav-cta--desktop { display: none; - flex-direction: column; - align-items: stretch; - gap: 0.75rem; - padding: 1.25rem; - background: rgba(12, 22, 52, 0.86); - backdrop-filter: blur(16px) saturate(160%); - -webkit-backdrop-filter: blur(16px) saturate(160%); - border: 1px solid rgba(96, 165, 250, 0.16); - border-radius: 1rem; - box-shadow: var(--shadow); - z-index: 20; - overflow: hidden; - isolation: isolate; } - .nav-list-container::before { - content: ""; - position: absolute; - inset: 0; - background: linear-gradient( - 180deg, - rgba(12, 22, 52, 0.98) 0%, - rgba(12, 22, 52, 0.94) 60%, - rgba(12, 22, 52, 0.05) 100% - ); - z-index: -1; + .nav-cta--mobile { + display: inline-flex; + width: 100%; + } + + .nav-list-container { + position: fixed; + left: 1rem; + right: 1rem; + top: 5.35rem; + flex-direction: column; + align-items: stretch; + padding: 1rem; + border: 1px solid var(--ww-line-strong); + border-radius: 1.25rem; + background: rgba(8, 23, 47, 0.96); + box-shadow: var(--ww-shadow); + opacity: 0; + pointer-events: none; + transform: translateY(-8px); + transition: + opacity 0.18s ease, + transform 0.18s ease; } - .site-header--open .nav-list-container { - display: flex; + .nav-list-container.is-open { + opacity: 1; + pointer-events: auto; + transform: translateY(0); } .nav-list { flex-direction: column; align-items: stretch; - gap: 0.5rem; } - .nav-list li, - .nav-link { + .link-button--nav { + justify-content: flex-start; width: 100%; } - .nav-link { - justify-content: center; + .result-card, + .publication-year-block, + .publication-card { + grid-template-columns: 1fr; } - .nav-link--cta-desktop { - display: none; + .result-card__result { + text-align: left; } - .nav-link--cta-mobile { - display: inline-flex; - width: 100%; - justify-content: center; + .publication-card__meta { + gap: 0.15rem; } - .footer-inner { - gap: 0.75rem; + .page-hero__metrics, + .metric-strip, + .pillar-grid, + .publication-preview-grid, + .social-grid { + grid-template-columns: 1fr; } -} -@media (max-width: 640px) { - .logo { - height: 3.2em; + .metric-strip__item + .metric-strip__item { + border-left: 0; + border-top: 1px solid var(--ww-line); } - .headline-row { - width: 100%; - flex-wrap: wrap; + .home-hero__floating-card { + left: 1rem; + right: 1rem; + bottom: 1rem; + max-width: none; } - .hero-like { - padding-top: 6rem; - padding-bottom: 6rem; + .contact-banner { + flex-direction: column; + align-items: flex-start; + } +} + +@media (max-width: 720px) { + .site-container { + width: min(1180px, calc(100% - 1.25rem)); } - .logo-hero { - height: 6rem; - padding-right: 0; + .logo { + height: 2.85rem; } - .grid { + .home-hero__image-frame { + min-height: 28rem; + } + + .team-showcase__photos { grid-template-columns: 1fr; } - .footer-inner { - flex-direction: column; - align-items: center; - text-align: center; + .media-panel--tall, + .media-panel--wide { + min-height: 18rem; } - .pub-item { - flex-direction: column; - align-items: flex-start; - gap: 0.75rem; + .photo-collage { + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-auto-rows: 9rem; } - .pub-actions { - margin-left: 0; - width: 100%; - display: flex; - flex-wrap: wrap; - gap: 0.5rem; + .photo-collage__item--wide, + .photo-collage__item--square, + .photo-collage__item--tall, + .photo-collage__item--panorama { + grid-column: span 1; + grid-row: span 1; } +} - .pdf-button, - .pub-link { - width: auto; - justify-content: flex-start; +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; } } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 030882d..0404437 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata, Viewport } from "next"; import SiteHeader from "@/components/SiteHeader"; +import SiteFooter from "@/components/SiteFooter"; import "./globals.scss"; const logoSingleDark = new URL( @@ -8,9 +9,9 @@ const logoSingleDark = new URL( ).toString(); export const metadata: Metadata = { - title: "Team whIRLwind", + title: "whIRLwind Amsterdam", description: - "Humanoid robotics team from the University of Amsterdam Intelligent Robotics Lab.", + "Humanoid robotics team from the Intelligent Robotics Lab at the University of Amsterdam.", icons: { icon: [ { url: logoSingleDark, type: "image/svg+xml" }, @@ -24,14 +25,14 @@ export const metadata: Metadata = { }, openGraph: { type: "website", - title: "Team whIRLwind at the University of Amsterdam", + title: "whIRLwind Amsterdam", description: - "Humanoid robotics team from the University of Amsterdam Intelligent Robotics Lab.", + "Humanoid robotics team from the Intelligent Robotics Lab at the University of Amsterdam.", }, }; export const viewport: Viewport = { - themeColor: "#000000", + themeColor: "#0a1833", }; export default function RootLayout({ @@ -41,17 +42,13 @@ export default function RootLayout({ }>) { return ( - + ); } diff --git a/src/app/socials/page.tsx b/src/app/socials/page.tsx index 2efaa5f..0f9361b 100644 --- a/src/app/socials/page.tsx +++ b/src/app/socials/page.tsx @@ -1,231 +1,63 @@ import type { Metadata } from "next"; -import Image, { type StaticImageData } from "next/image"; -import Link from "next/link"; -import PhotoBackstage from "@/assets/photos/158-IMG_5111.jpg"; -import PhotoWorkshop from "@/assets/photos/168-IMG_5126.jpg"; -import PhotoEvening from "@/assets/photos/20250812_202120.jpg"; -import PhotoNaoLab from "@/assets/photos/203-IMG_5141.jpg"; -import PhotoTeam from "@/assets/photos/5-IMG_5253.jpg"; -import PhotoIceRibbon from "@/assets/photos/NAN12947_3610623519-rp3913954625-opq3915027514.jpg"; -import PhotoShowcase from "@/assets/photos/NAN12360-opq3912652044.jpg"; -import PhotoTeamWithPortugeseProfessor from "@/assets/photos/NAN13334_3613110639-rp3914172363-opq3915542285.jpg"; -import PhotoRobot from "@/assets/photos/NAN18608-opq3906025937.jpg"; -import PhotoGermanOpen2025 from "@/assets/photos/DSC_0798.jpg"; -import PhotoFreeKick from "@/assets/photos/ROC_4057-opq3912329047.jpg"; -import PhotoLaptopOnField from "@/assets/photos/110-DSC09284.jpg"; -import PhotoTeamWorkingAtPitch from "@/assets/photos/117-DSC09277.jpg"; -import PhotoRobotWalking from "@/assets/photos/133-DSC09261.jpg"; -import PhotoTeamSetup from "@/assets/photos/55142547581_39130691ce_o.jpg"; -import PhotoMemberWithRobot from "@/assets/photos/72-DSC09322.jpg"; -import PhotoWhirlwindJersey from "@/assets/photos/74-DSC09320.jpg"; -import PhotoRobotsFromBehind from "@/assets/photos/86-DSC09308.jpg"; -import GitHubLogo from "@/assets/socials/github.svg"; -import InstagramLogo from "@/assets/socials/instagram.svg"; -import LinkedInLogo from "@/assets/socials/linkedin.svg"; +import Image from "next/image"; +import LinkButton from "@/components/LinkButton"; +import PageHero from "@/components/site/PageHero"; +import PhotoCollage from "@/components/site/PhotoCollage"; +import SocialCard from "@/components/site/SocialCard"; +import { + homePhotos, + socialChannels, + socialCollagePhotos, +} from "@/lib/site-content"; export const metadata: Metadata = { title: "Socials | Team whIRLwind", description: "Follow Team whIRLwind on our social media channels.", }; -type SocialChannel = { - name: string; - url: string; - handle?: string; - logo: StaticImageData; - logoAlt: string; - description: string; -}; - -type CollagePhoto = { - src: StaticImageData; - alt: string; - layout: "wide" | "tall" | "square" | "panorama"; -}; - -const socialChannels: SocialChannel[] = [ - { - name: "LinkedIn", - url: "https://www.linkedin.com/company/whirlwind-amsterdam/", - handle: "whIRLwind Amsterdam", - logo: LinkedInLogo, - logoAlt: "LinkedIn logo", - description: "Announcements, partnerships, and team stories.", - }, - { - name: "Instagram", - url: "https://www.instagram.com/whirlwind.ams", - handle: "@whirlwind.ams", - logo: InstagramLogo, - logoAlt: "Instagram logo", - description: "Travel stories and day-to-day progress from the team.", - }, - { - name: "GitHub", - url: "https://github.com/IntelligentRoboticsLab", - handle: "Intelligent Robotics Lab", - logo: GitHubLogo, - logoAlt: "GitHub logo", - description: "Code releases, tools, and research repos.", - }, -]; - -const collagePhotos: CollagePhoto[] = [ - { - src: PhotoLaptopOnField, - alt: "Team member kneeling on the field debugging robots with a laptop.", - layout: "wide", - }, - { - src: PhotoTeamWorkingAtPitch, - alt: "Two team members working on a robot at pitch level during a match.", - layout: "square", - }, - { - src: PhotoRobotWalking, - alt: "Team member guiding a walking robot near the goal.", - layout: "tall", - }, - { - src: PhotoTeamSetup, - alt: "The full team set up at the German Open 2026 with the RoboCup World Championship 2027 sign.", - layout: "panorama", - }, - { - src: PhotoMemberWithRobot, - alt: "Team member standing with a robot on the football field.", - layout: "wide", - }, - { - src: PhotoWhirlwindJersey, - alt: "Back of a whIRLwind 2026 team jersey showing sponsors.", - layout: "square", - }, - { - src: PhotoRobotsFromBehind, - alt: "Two robots in red jerseys seen from behind during a match.", - layout: "wide", - }, - { - src: PhotoRobot, - alt: "Close-up of the robot under bright lighting.", - layout: "wide", - }, - { - src: PhotoTeamWithPortugeseProfessor, - alt: "Team photo with the professor of the Portugese team in Beijing", - layout: "wide", - }, - { - src: PhotoFreeKick, - alt: "Robots waiting to take a free kick", - layout: "tall", - }, - { - src: PhotoWorkshop, - alt: "Team members giving a demo to visitors in the Lab", - layout: "tall", - }, - { - src: PhotoGermanOpen2025, - alt: "Team looking on during a match at the German Open 2025", - layout: "panorama", - }, - { - src: PhotoNaoLab, - alt: "The team showing a NAO robot to visitors in the Lab", - layout: "square", - }, - { - src: PhotoEvening, - alt: "Preparing for a match during evening competitions.", - layout: "square", - }, - { - src: PhotoShowcase, - alt: "Team posing with the robot at the World Humanoid Robot Games showcase.", - layout: "wide", - }, - { - src: PhotoTeam, - alt: "The team looking on as the robot walks around", - layout: "tall", - }, - { - src: PhotoBackstage, - alt: "Team members repairing a broken ankle joint", - layout: "wide", - }, - { - src: PhotoIceRibbon, - alt: "The Ice Ribbon competition venu in Bejing, China", - layout: "square", - }, -]; - -function isExternalUrl(url: string): boolean { - return /^https?:\/\//.test(url); -} - export default function SocialsPage() { return ( -
-
-

Socials

-
- {socialChannels.map((platform) => { - const isExternal = isExternalUrl(platform.url); - return ( - -
- {platform.logoAlt} -
-

{platform.name}

- {platform.handle && ( -

{platform.handle}

- )} -
-
- -

- {platform.description} -

- - ); - })} -
-
- {collagePhotos.map((photo, index) => ( -
- {photo.alt} -
+
+ + Where to +
+ follow along. + + } + description="Where we post competition travel, demos, progress, and the code behind it." + actions={ + + } + aside={ +
+ {homePhotos.supportA.alt} +
+ } + /> + +
+
+ {socialChannels.map((channel) => ( + ))}
-
-
+ + +
+
+ +
+
+ ); } diff --git a/src/app/sponsors/page.tsx b/src/app/sponsors/page.tsx index 36e233c..dc6ca73 100644 --- a/src/app/sponsors/page.tsx +++ b/src/app/sponsors/page.tsx @@ -1,94 +1,89 @@ import type { Metadata } from "next"; -import SponsorCard, { type Sponsor } from "@/components/SponsorCard"; -import rerunLogo from "@/assets/sponsors/rerun.svg"; -import startupVillageLogo from "@/assets/sponsors/startup_village.webp"; -import uvaLogo from "@/assets/sponsors/uva.png"; +import LinkButton from "@/components/LinkButton"; +import SponsorCard from "@/components/SponsorCard"; +import PageHero from "@/components/site/PageHero"; +import { sponsorTiers } from "@/lib/site-content"; export const metadata: Metadata = { title: "Sponsors | Team whIRLwind", description: "Organisations supporting Team whIRLwind.", }; -type SponsorTier = { - name: string; - sponsors: Sponsor[]; -}; - -const sponsorTiers: SponsorTier[] = [ - { - name: "Premier Partner", - sponsors: [ - { - name: "Rerun", - website: "https://rerun.io", - logo: rerunLogo, - logoAlt: "Rerun logo", - logoWidth: 94, - logoHeight: 28, - logoMaxWidth: "420px", - logoDisplayHeight: "96px", - captionGapClass: "gap-2", - logoWrapperClassName: "px-4 pt-2 pb-1", - }, - ], - }, - { - name: "Supporting Partners", - sponsors: [ - { - name: "StartUp Village", - website: "https://startupvillage.nl", - logo: startupVillageLogo, - logoAlt: "StartUp Village logo", - logoWidth: 480, - logoHeight: 242, - }, - { - name: "University of Amsterdam", - website: "https://uva.nl", - logo: uvaLogo, - logoAlt: "University of Amsterdam logo", - logoWidth: 480, - logoHeight: 242, - }, - ], - }, -]; - export default function SponsorsPage() { return ( -
-
-

- Sponsors -

-

- These partners keep our robots rolling and make it possible to share - the work beyond the lab. -

- -
- {sponsorTiers.map((tier) => { - const tierClasses = "sponsor-entry space-y-8"; - const rowClasses = [ - "flex flex-col gap-4", - tier.sponsors.length > 1 - ? "sm:flex-row sm:flex-wrap sm:gap-6" - : "", - ].join(" "); +
+ + Who keeps us +
+ on the field. + + } + description="The organisations that help us build the robots and get them to competitions." + metrics={[ + { label: "Partner tiers", value: `${sponsorTiers.length}` }, + { + label: "Current partners", + value: `${sponsorTiers.reduce((sum, tier) => sum + tier.sponsors.length, 0)}`, + }, + { label: "Base", value: "Amsterdam" }, + ]} + actions={ + + } + aside={ +
+

What it covers

+

Hardware, travel, tooling, and the lab space.

+ Running a competition team adds up quickly. +
+ } + /> - return ( -
-
- {tier.sponsors.map((sponsor) => ( - - ))} -
+
+
+ {sponsorTiers.map((tier) => ( +
+
+

{tier.name}

+ {tier.description}
- ); - })} +
+ {tier.sponsors.map((sponsor) => ( + + ))} +
+
+ ))} +
+
+ +
+
+
+ Sponsor us +

+ Want to sponsor? +

+

+ Email us. We can figure out a sponsorship that fits. +

+
+
+ +
-
-
+ + ); } diff --git a/src/components/BibtexButton.tsx b/src/components/BibtexButton.tsx index e94702d..8d50792 100644 --- a/src/components/BibtexButton.tsx +++ b/src/components/BibtexButton.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { IPublication, toBibtex } from "@/lib/publications/publication"; +import SiteIcon from "./site/SiteIcon"; interface BibtexButtonProps { publication: IPublication; @@ -22,76 +23,15 @@ export default function BibtexButton({ publication }: BibtexButtonProps) { }; return ( - <> - - - + ); } diff --git a/src/components/LinkButton.tsx b/src/components/LinkButton.tsx index b354fec..97acf23 100644 --- a/src/components/LinkButton.tsx +++ b/src/components/LinkButton.tsx @@ -6,7 +6,8 @@ type LinkButtonProps = { onNavigate?: () => void; className?: string; ariaLabel?: string; - bordered?: boolean; + variant?: "nav" | "primary" | "secondary" | "inline"; + active?: boolean; }; export default function LinkButton({ @@ -15,28 +16,27 @@ export default function LinkButton({ onNavigate, className, ariaLabel, - bordered = false, + variant = "nav", + active = false, }: LinkButtonProps) { - const classes = ["nav-link"]; - - if (bordered) { - classes.push("nav-link--bordered"); - } - - if (className) { - classes.push(className); - } + const classes = [ + "link-button", + `link-button--${variant}`, + active ? "is-active" : "", + className ?? "", + ] + .filter(Boolean) + .join(" "); return ( -