diff --git a/astro.config.mjs b/astro.config.mjs index e762ba5..53e49de 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,5 +1,9 @@ // @ts-check import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; + // https://astro.build/config -export default defineConfig({}); +export default defineConfig({ + integrations: [react()] +}); \ No newline at end of file diff --git a/functions/contact.ts b/functions/contact.ts new file mode 100644 index 0000000..c286d28 --- /dev/null +++ b/functions/contact.ts @@ -0,0 +1,147 @@ +export const onRequestPost: PagesFunction = async ({ request, env }) => { + console.log('📨 Recibiendo petición de contacto...'); + + try { + const contentType = request.headers.get('content-type') || ''; + console.log('Content-Type:', contentType); + let data: Record = {}; + + if (contentType.includes('application/json')) { + data = await request.json(); + console.log('✅ Datos JSON parseados'); + } else if (contentType.includes('application/x-www-form-urlencoded')) { + const form = await request.formData(); + data = Object.fromEntries([...form.entries()].map(([k, v]) => [k, String(v)])); + console.log('✅ Datos form-data parseados'); + } else { + console.error('❌ Content-type no soportado:', contentType); + return new Response(JSON.stringify({ ok: false, error: 'Unsupported content type' }), { + status: 415, + headers: { 'content-type': 'application/json' }, + }); + } + + const { nombre, email, asunto, mensaje } = data; + console.log('Datos recibidos:', { nombre, email, asunto, mensajeLength: mensaje?.length }); + + if (!nombre || !email || !asunto || !mensaje) { + console.error('❌ Campos faltantes'); + return new Response(JSON.stringify({ ok: false, error: 'Missing fields' }), { + status: 400, + headers: { 'content-type': 'application/json' }, + }); + } + + // Basic email validation + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + console.error('❌ Email inválido:', email); + return new Response(JSON.stringify({ ok: false, error: 'Invalid email' }), { + status: 400, + headers: { 'content-type': 'application/json' }, + }); + } + + // Validar Turnstile + const turnstileToken = data['cf-turnstile-response']; + if (!turnstileToken) { + console.error('❌ Token de Turnstile faltante'); + return new Response(JSON.stringify({ ok: false, error: 'Turnstile token missing' }), { + status: 400, + headers: { 'content-type': 'application/json' }, + }); + } + + const ip = request.headers.get('CF-Connecting-IP'); + const formData = new FormData(); + formData.append('secret', env.TURNSTILE_SECRET_KEY || '1x0000000000000000000000000000000AA'); + formData.append('response', turnstileToken); + formData.append('remoteip', ip || ''); + + const turnstileUrl = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; + const turnstileResult = await fetch(turnstileUrl, { + body: formData, + method: 'POST', + }); + + const turnstileOutcome: any = await turnstileResult.json(); + if (!turnstileOutcome.success) { + console.error('❌ Validación de Turnstile fallida:', turnstileOutcome); + return new Response(JSON.stringify({ ok: false, error: 'Invalid Turnstile token' }), { + status: 403, + headers: { 'content-type': 'application/json' }, + }); + } + + // Enviar mensaje via Telegram Bot + console.log('🔍 Verificando variables de Telegram...'); + console.log('TELEGRAM_BOT_TOKEN presente:', !!env.TELEGRAM_BOT_TOKEN); + console.log('TELEGRAM_CHAT_ID presente:', !!env.TELEGRAM_CHAT_ID); + + if (!env.TELEGRAM_BOT_TOKEN || !env.TELEGRAM_CHAT_ID) { + console.error('❌ Variables de entorno de Telegram NO configuradas'); + return new Response(JSON.stringify({ + ok: false, + error: 'Telegram no configurado. Contacta al administrador.' + }), { + status: 500, + headers: { 'content-type': 'application/json' }, + }); + } + + const tgApi = `https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage`; + const text = [ + `✨ Nuevo contacto en PGSCOM`, + ``, + `👤 Nombre: ${nombre}`, + `📧 Email: ${email}`, + `📝 Asunto: ${asunto}`, + ``, + `💬 Mensaje:`, + `${mensaje}`, + ``, + `🕐 ${new Date().toLocaleString('es-ES', { timeZone: 'Europe/Madrid' })}`, + ].join('\n'); + + console.log('📤 Enviando mensaje a Telegram...'); + const tgResp = await fetch(tgApi, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + chat_id: env.TELEGRAM_CHAT_ID, + text, + disable_web_page_preview: true, + }), + }); + + const tgData = await tgResp.json(); + console.log('Respuesta de Telegram:', { status: tgResp.status, ok: tgData.ok }); + + if (!tgResp.ok || !tgData.ok) { + console.error('❌ Error de Telegram:', JSON.stringify(tgData)); + return new Response(JSON.stringify({ + ok: false, + error: 'Error al enviar mensaje a Telegram' + }), { + status: 500, + headers: { 'content-type': 'application/json' }, + }); + } + + console.log('✅ Mensaje enviado exitosamente a Telegram'); + return new Response(JSON.stringify({ ok: true }), { + status: 200, + headers: { 'content-type': 'application/json' }, + }); + } catch (error) { + console.error('❌ Error en la función:', error); + return new Response(JSON.stringify({ + ok: false, + error: 'Server error', + details: error instanceof Error ? error.message : String(error) + }), { + status: 500, + headers: { 'content-type': 'application/json' }, + }); + } +}; diff --git a/package-lock.json b/package-lock.json index 130f81e..9cfc404 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,14 @@ "name": "pgscom.github.io", "version": "0.0.1", "dependencies": { + "@astrojs/react": "^4.4.0", + "@types/react": "^19.1.16", + "@types/react-dom": "^19.1.9", "animejs": "^4.0.2", - "astro": "^5.12.9" + "astro": "^5.15.1", + "liquid-glass-react": "^1.1.1", + "react": "^19.1.1", + "react-dom": "^19.1.1" } }, "node_modules/@astrojs/compiler": { @@ -19,18 +25,18 @@ "license": "MIT" }, "node_modules/@astrojs/internal-helpers": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.3.tgz", - "integrity": "sha512-6Pl0bQEIChuW5wqN7jdKrzWfCscW2rG/Cz+fzt4PhSQX2ivBpnhXgFUCs0M3DCYvjYHnPVG2W36X5rmFjZ62sw==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.4.tgz", + "integrity": "sha512-lDA9MqE8WGi7T/t2BMi+EAXhs4Vcvr94Gqx3q15cFEz8oFZMO4/SFBqYr/UcmNlvW+35alowkVj+w9VhLvs5Cw==", "license": "MIT" }, "node_modules/@astrojs/markdown-remark": { - "version": "6.3.7", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.7.tgz", - "integrity": "sha512-KXGdq6/BC18doBCYXp08alHlWChH0hdD2B1qv9wIyOHbvwI5K6I7FhSta8dq1hBQNdun8YkKPR013D/Hm8xd0g==", + "version": "6.3.8", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.8.tgz", + "integrity": "sha512-uFNyFWadnULWK2cOw4n0hLKeu+xaVWeuECdP10cQ3K2fkybtTlhb7J7TcScdjmS8Yps7oje9S/ehYMfZrhrgCg==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.7.3", + "@astrojs/internal-helpers": "0.7.4", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", @@ -44,7 +50,7 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", - "shiki": "^3.12.2", + "shiki": "^3.13.0", "smol-toml": "^1.4.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", @@ -65,6 +71,26 @@ "node": "18.20.8 || ^20.3.0 || >=22.0.0" } }, + "node_modules/@astrojs/react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-4.4.0.tgz", + "integrity": "sha512-RzblkVImAFdV1C0AWsSWzS70Z0FMtW2p0XXkNYu3QePfyVJta3JIy8m8jY8271etaCZtpFjsE2UaiHGZIBm6nw==", + "license": "MIT", + "dependencies": { + "@vitejs/plugin-react": "^4.7.0", + "ultrahtml": "^1.6.0", + "vite": "^6.3.6" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + }, + "peerDependencies": { + "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", + "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", + "react": "^17.0.2 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", @@ -83,6 +109,166 @@ "node": "18.20.8 || ^20.3.0 || >=22.0.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -101,13 +287,35 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -116,10 +324,72 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -130,14 +400,15 @@ } }, "node_modules/@capsizecss/unpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-2.4.0.tgz", - "integrity": "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-3.0.0.tgz", + "integrity": "sha512-+ntATQe1AlL7nTOYjwjj6w3299CgRot48wL761TUGYpYgAou3AaONZazp0PKZyCyWhudWsjhq1nvRHOvbMzhTA==", "license": "MIT", "dependencies": { - "blob-to-buffer": "^1.2.8", - "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" + }, + "engines": { + "node": ">=18" } }, "node_modules/@emnapi/runtime": { @@ -994,18 +1265,63 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", "license": "MIT" }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "license": "MIT" + }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", @@ -1396,6 +1712,47 @@ "tslib": "^2.8.0" } }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -1462,6 +1819,24 @@ "undici-types": "~7.8.0" } }, + "node_modules/@types/react": { + "version": "19.1.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.16.tgz", + "integrity": "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1474,6 +1849,26 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1617,16 +2012,16 @@ } }, "node_modules/astro": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.14.1.tgz", - "integrity": "sha512-gPa8NY7/lP8j8g81iy8UwANF3+aukKRWS68IlthZQNgykpg80ne6lbHOp6FErYycxQ1TUhgEfkXVDQZAoJx8Bg==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.15.1.tgz", + "integrity": "sha512-VM679M1qxOjGo6q3vKYDNDddkALGgMopG93IwbEXd3Buc2xVLuuPj4HNziNugSbPQx5S6UReMp5uzw10EJN81A==", "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.12.2", - "@astrojs/internal-helpers": "0.7.3", - "@astrojs/markdown-remark": "6.3.7", + "@astrojs/internal-helpers": "0.7.4", + "@astrojs/markdown-remark": "6.3.8", "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^2.4.0", + "@capsizecss/unpack": "^3.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.2.0", "acorn": "^8.15.0", @@ -1654,7 +2049,6 @@ "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", - "kleur": "^4.1.5", "magic-string": "^0.30.18", "magicast": "^0.3.5", "mrmime": "^2.0.1", @@ -1662,17 +2056,18 @@ "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.3.0", + "picocolors": "^1.1.1", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.2", "shiki": "^3.12.0", "smol-toml": "^1.4.2", - "tinyexec": "^0.3.2", + "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", - "unifont": "~0.5.2", + "unifont": "~0.6.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.0", "vfile": "^6.0.3", @@ -1746,25 +2141,14 @@ ], "license": "MIT" }, - "node_modules/blob-to-buffer": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", - "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/baseline-browser-mapping": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } }, "node_modules/boxen": { "version": "8.0.1", @@ -1797,6 +2181,39 @@ "base64-js": "^1.1.2" } }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/camelcase": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", @@ -1809,6 +2226,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -1937,6 +2374,12 @@ "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", "license": "ISC" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, "node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -1952,15 +2395,6 @@ "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", "license": "MIT" }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/crossws": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", @@ -1995,6 +2429,12 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -2117,6 +2557,12 @@ "node": ">=4" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.227", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", + "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -2182,6 +2628,15 @@ "@esbuild/win32-x64": "0.25.10" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -2288,6 +2743,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-east-asian-width": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", @@ -2620,6 +3084,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2632,15 +3102,43 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, "engines": { "node": ">=6" } }, + "node_modules/liquid-glass-react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/liquid-glass-react/-/liquid-glass-react-1.1.1.tgz", + "integrity": "sha512-pKzaktaMAEztd93wpWcz2Z5Z9qdLJUNJdMX+n00Ca4XsnrLTQ5xJzm/+GQXZUeuFXe/PQ8ziVMZO6531PyaFJw==", + "license": "MIT", + "workspaces": [ + "liquid-glass" + ], + "peerDependencies": { + "react": ">=19", + "react-dom": ">=19" + } + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -3536,26 +4034,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-fetch-native": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", @@ -3568,6 +4046,12 @@ "integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", "license": "MIT" }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3789,6 +4273,36 @@ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", "license": "MIT" }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4076,6 +4590,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, "node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -4237,9 +4757,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", "license": "MIT" }, "node_modules/tinyglobby": { @@ -4258,12 +4778,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -4400,9 +4914,9 @@ } }, "node_modules/unifont": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.5.2.tgz", - "integrity": "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.6.0.tgz", + "integrity": "sha512-5Fx50fFQMQL5aeHyWnZX9122sSLckcDvcfFiBf3QYeHa7a1MKJooUy52b67moi2MJYkrfo/TWY+CoLdr/w0tTA==", "license": "MIT", "dependencies": { "css-tree": "^3.0.0", @@ -4629,6 +5143,36 @@ } } }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -4774,22 +5318,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which-pm-runs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", @@ -4837,6 +5365,12 @@ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", "license": "MIT" }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", diff --git a/package.json b/package.json index 0b767bc..f037710 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,13 @@ "astro": "astro" }, "dependencies": { + "@astrojs/react": "^4.4.0", + "@types/react": "^19.1.16", + "@types/react-dom": "^19.1.9", "animejs": "^4.0.2", - "astro": "^5.12.9" + "astro": "^5.15.1", + "liquid-glass-react": "^1.1.1", + "react": "^19.1.1", + "react-dom": "^19.1.1" } } diff --git a/src/assets/animlogo/logo.svg b/public/assets/animlogo/logo.svg similarity index 100% rename from src/assets/animlogo/logo.svg rename to public/assets/animlogo/logo.svg diff --git a/public/assets/proyectos/Blog.png b/public/assets/proyectos/Blog.png new file mode 100644 index 0000000..c132291 Binary files /dev/null and b/public/assets/proyectos/Blog.png differ diff --git a/public/assets/proyectos/Contribuciones.png b/public/assets/proyectos/Contribuciones.png new file mode 100644 index 0000000..65dbd45 Binary files /dev/null and b/public/assets/proyectos/Contribuciones.png differ diff --git a/public/assets/proyectos/Herramientas.png b/public/assets/proyectos/Herramientas.png new file mode 100644 index 0000000..daa4e44 Binary files /dev/null and b/public/assets/proyectos/Herramientas.png differ diff --git a/public/assets/proyectos/Macula.png b/public/assets/proyectos/Macula.png new file mode 100644 index 0000000..0fa574a Binary files /dev/null and b/public/assets/proyectos/Macula.png differ diff --git a/public/assets/proyectos/PortfolioCortos.png b/public/assets/proyectos/PortfolioCortos.png new file mode 100644 index 0000000..8e00c9c Binary files /dev/null and b/public/assets/proyectos/PortfolioCortos.png differ diff --git a/public/assets/proyectos/UI.png b/public/assets/proyectos/UI.png new file mode 100644 index 0000000..853329a Binary files /dev/null and b/public/assets/proyectos/UI.png differ diff --git a/public/assets/proyectos/portfolio.png b/public/assets/proyectos/portfolio.png new file mode 100644 index 0000000..fb8b6a4 Binary files /dev/null and b/public/assets/proyectos/portfolio.png differ diff --git a/public/icons/animation.svg b/public/icons/animation.svg new file mode 100644 index 0000000..ee3659c --- /dev/null +++ b/public/icons/animation.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/blog.svg b/public/icons/blog.svg new file mode 100644 index 0000000..284a6a7 --- /dev/null +++ b/public/icons/blog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/code.svg b/public/icons/code.svg new file mode 100644 index 0000000..75c571a --- /dev/null +++ b/public/icons/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/design.svg b/public/icons/design.svg new file mode 100644 index 0000000..86def0d --- /dev/null +++ b/public/icons/design.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/discord.svg b/public/icons/discord.svg new file mode 100644 index 0000000..9ade36c --- /dev/null +++ b/public/icons/discord.svg @@ -0,0 +1 @@ +Discord \ No newline at end of file diff --git a/public/icons/github.svg b/public/icons/github.svg new file mode 100644 index 0000000..6c87ae9 --- /dev/null +++ b/public/icons/github.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/icons/instagram.svg b/public/icons/instagram.svg new file mode 100644 index 0000000..1ff4423 --- /dev/null +++ b/public/icons/instagram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/photo.svg b/public/icons/photo.svg new file mode 100644 index 0000000..33b96e1 --- /dev/null +++ b/public/icons/photo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/twitter.svg b/public/icons/twitter.svg new file mode 100644 index 0000000..1c3c995 --- /dev/null +++ b/public/icons/twitter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/icons/ux.svg b/public/icons/ux.svg new file mode 100644 index 0000000..1dcd50e --- /dev/null +++ b/public/icons/ux.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/video.svg b/public/icons/video.svg new file mode 100644 index 0000000..fabf624 --- /dev/null +++ b/public/icons/video.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icons/web.svg b/public/icons/web.svg new file mode 100644 index 0000000..da37186 --- /dev/null +++ b/public/icons/web.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/youtube.svg b/public/icons/youtube.svg new file mode 100644 index 0000000..303bd7d --- /dev/null +++ b/public/icons/youtube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/liquidglasscomponent.jsx b/src/components/liquidglasscomponent.jsx new file mode 100644 index 0000000..0dfd2ee --- /dev/null +++ b/src/components/liquidglasscomponent.jsx @@ -0,0 +1,86 @@ +import React, { useRef, useState, useEffect } from 'react'; +// liquid-glass-react exports a CommonJS module. Import the default and +// extract LiquidGlass safely to support both ESM and CJS interop. +import _pkg from 'liquid-glass-react'; +const LiquidGlass = (_pkg && (_pkg.LiquidGlass || _pkg.default || _pkg)) || null; + +// Acepta `children` como una prop +export default function LiquidGlassComponent({ children }) { + const containerRef = useRef(null); + const [isMobile, setIsMobile] = useState(true); // Por defecto móvil para SSR + const [isClient, setIsClient] = useState(false); + + // Detectar si estamos en el cliente y si es móvil + useEffect(() => { + setIsClient(true); + + // Detectar dispositivos móviles de manera más completa + const checkMobile = () => { + const userAgent = navigator.userAgent || navigator.vendor || window.opera; + const isMobileUA = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent.toLowerCase()); + const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; + const isSmallScreen = window.innerWidth <= 1024; // Aumentado para tablets también + + return isMobileUA || (isTouchDevice && isSmallScreen); + }; + + setIsMobile(checkMobile()); + + // NO actualizar en resize para evitar re-renders costosos + }, []); + + // Fallback ULTRA-LIGERO para móviles - SIN backdrop-filter, SIN efectos pesados + const MobileFallback = () => ( +
+ {children} +
+ ); + + // Si no se encontró el componente o estamos en el servidor, renderizamos fallback + if (!LiquidGlass || !isClient) { + return ; + } + + // En móviles, usar fallback CSS ultra-ligero (sin WebGL, sin backdrop-filter) + if (isMobile) { + return ; + } + + // En desktop, usar el efecto completo pero muy optimizado + return ( + + {children} + + + ); +} \ No newline at end of file diff --git a/src/data/contacto.json b/src/data/contacto.json new file mode 100644 index 0000000..f6a359e --- /dev/null +++ b/src/data/contacto.json @@ -0,0 +1,37 @@ +{ + "informacion": { + "email": "contacto@pgscom.es" + }, + "redes": [ + { + "nombre": "GitHub", + "icono": "/icons/github.svg", + "url": "https://github.com/PGSCOM" + }, + { + "nombre": "Blog", + "icono": "/icons/blog.svg", + "url": "https://blog.pgscom.es/" + }, + { + "nombre": "Twitter", + "icono": "/icons/twitter.svg", + "url": "https://twitter.com/pgscom_54" + }, + { + "nombre": "Instagram", + "icono": "/icons/instagram.svg", + "url": "https://instagram.com/pgscom" + }, + { + "nombre": "Discord", + "icono": "/icons/discord.svg", + "url": "https://discord.com/invite/U5jP9uHkEC" + }, + { + "nombre": "YouTube", + "icono": "/icons/youtube.svg", + "url": "https://www.youtube.com/@pgscom_54" + } + ] +} diff --git a/src/data/proyectos.json b/src/data/proyectos.json new file mode 100644 index 0000000..db5b53a --- /dev/null +++ b/src/data/proyectos.json @@ -0,0 +1,200 @@ +[ + { + "id": "desarrollo-web", + "titulo": "Desarrollo Web", + "logo": "/icons/web.svg", + "descripcion": "Desarrollo de sitios y aplicaciones web con tecnologías modernas", + "proyectos": [ + { + "id": "portfolio-pgscom", + "titulo": "Portfolio PGSCOM", + "imagen": "/assets/proyectos/portfolio.png", + "descripcion": "Sitio web personal desarrollado con Astro, React y animaciones con AnimeJS.", + "tecnologias": ["Astro", "React", "TypeScript", "AnimeJS", "CSS3"], + "link": "https://pgscom.es", + "contenido": "

Portfolio Personal PGSCOM

Este proyecto representa mi carta de presentación digital, desarrollado completamente desde cero con las tecnologías más modernas.

Características principales:

  • Astro 4.0 - Framework ultra-rápido
  • React - Componentes interactivos
  • AnimeJS - Animaciones fluidas
  • Diseño Responsivo
" + }, + { + "id": "blog-personal", + "titulo": "Blog Personal", + "imagen": "/assets/proyectos/Blog.png", + "descripcion": "Blog técnico sobre audiovisual, tecnología y programación.", + "tecnologias": ["HTML", "CSS", "JavaScript"], + "link": "https://blog.pgscom.es", + "contenido": "

Blog Técnico Personal

Espacio dedicado a compartir conocimientos sobre producción audiovisual y programación.

Temáticas:

  • Tutoriales de edición
  • Guías de programación
  • Consejos de diseño
" + }, + { + "id": "portafolio-cine", + "titulo": "Portafolio Cinematográfico", + "imagen": "/assets/proyectos/PortfolioCortos.png", + "descripcion": "Galería de proyectos audiovisuales con reproductor de video integrado.", + "tecnologias": ["HTML5", "CSS3", "JavaScript", "Video.js"], + "link": "https://cine.pgscom.es", + "contenido": "

Portafolio Cinematográfico

Galería especializada para mostrar mis proyectos audiovisuales.

Funcionalidades:

  • Reproductor personalizado
  • Sistema de categorías
  • Galería de stills
" + } + ] + }, + { + "id": "video-produccion", + "titulo": "Producción Audiovisual", + "logo": "/icons/video.svg", + "descripcion": "Cortometrajes, documentales y proyectos cinematográficos", + "proyectos": [ + { + "id": "premio-macula-2024", + "titulo": "Premio Mácula 2024 ", + "imagen": "/assets/proyectos/Macula.png", + "descripcion": "Segundo Premio Mácula 2024 - Cortometraje documental galardonado.", + "tecnologias": ["DaVinci Resolve", "Premiere Pro", "After Effects"], + "premio": "2º Premio Mácula 2024", + "contenido": "

Segundo Premio Mácula 2024

Cortometraje documental galardonado con el Segundo Premio.

Ficha técnica:

  • Formato: Documental
  • Duración: 12 minutos
  • Postproducción: DaVinci Resolve
\"Trabajo excepcional\" - Jurado Festival Mácula
" + }, + { + "id": "premio-macula-2023", + "titulo": "Premio Mácula 2023 ", + "imagen": "/assets/proyectos/Macula.png", + "descripcion": "Segundo Premio Mácula 2023 - Proyecto audiovisual experimental.", + "tecnologias": ["DaVinci Resolve", "Premiere Pro"], + "premio": "2º Premio Mácula 2023", + "contenido": "

Segundo Premio Mácula 2023

Proyecto audiovisual experimental con nuevas formas narrativas.

Características:

  • Montaje experimental
  • Paleta expresionista
  • Banda sonora original
" + }, + { + "id": "cortometrajes", + "titulo": "Cortometrajes Varios", + "imagen": "/assets/proyectos/PortfolioCortos.png", + "descripcion": "Colección de cortometrajes de ficción y documental.", + "tecnologias": ["DaVinci Resolve", "After Effects", "Audition"], + "contenido": "

Colección de Cortometrajes

Recopilación de proyectos audiovisuales en ficción y documental.

Roles:

  • Dirección de fotografía
  • Montaje
  • Corrección de color
" + } + ] + }, + { + "id": "animacion-3d", + "titulo": "Animación 3D", + "logo": "/icons/animation.svg", + "descripcion": "Modelado, animación y renders con Blender y Unreal Engine", + "proyectos": [ + { + "id": "logo-animado-pgscom", + "titulo": "Logo Animado PGSCOM", + "imagen": "/assets/proyectos/logo-anim.jpg", + "descripcion": "Animación 3D del logo personal con partículas y efectos visuales.", + "tecnologias": ["Blender", "Cycles", "After Effects"], + "contenido": "

Logo Animado PGSCOM

Animación 3D con sistema de partículas.

Especificaciones:

  • Render: Cycles GPU
  • Resolución: 4K
  • Frames: 120+
" + }, + { + "id": "visuales-unreal", + "titulo": "Entornos Virtuales", + "imagen": "/assets/proyectos/unreal.jpg", + "descripcion": "Creación de entornos virtuales fotorrealistas en Unreal Engine.", + "tecnologias": ["Unreal Engine", "Blender", "Substance Painter"], + "contenido": "

Entornos Virtuales

Escenarios fotorrealistas en Unreal Engine 5.

Tecnologías:

  • Lumen y Nanite
  • Texturizado PBR
  • Modelado en Blender
" + }, + { + "id": "motion-graphics", + "titulo": "Motion Graphics", + "imagen": "/assets/proyectos/motion.jpg", + "descripcion": "Piezas de motion graphics para redes sociales y proyectos comerciales.", + "tecnologias": ["Blender", "After Effects", "Illustrator"], + "contenido": "

Motion Graphics

Piezas animadas para redes y publicidad.

Servicios:

  • Contenido social
  • Intros/outros
  • Infografías animadas
" + } + ] + }, + { + "id": "programacion", + "titulo": "Proyectos de Programación", + "logo": "/icons/code.svg", + "descripcion": "Aplicaciones y scripts en Python, C++ y más", + "proyectos": [ + { + "id": "github-contributions", + "titulo": "Contribuciones Open Source", + "imagen": "/assets/proyectos/github.jpg", + "descripcion": "Más de 4 años de experiencia con Git/GitHub.", + "tecnologias": ["Git", "GitHub", "Python", "JavaScript"], + "link": "https://github.com/PGSCOM", + "contenido": "

Open Source

Participación activa en código abierto.

Estadísticas:

  • 20+ repositorios
  • 50+ estrellas
  • 30+ pull requests
" + }, + { + "id": "python-tools", + "titulo": "Herramientas Python", + "imagen": "/assets/proyectos/Herramientas.png", + "descripcion": "Scripts de automatización para proyectos audiovisuales. +6 años.", + "tecnologias": ["Python", "NumPy", "OpenCV", "FFmpeg"], + "contenido": "

Herramientas Python

Scripts de automatización audiovisual.

Proyectos:

  • Automatización FFmpeg
  • Procesamiento de metadatos
  • Batch de imágenes
" + }, + { + "id": "cpp-learning", + "titulo": "Aprendiendo C++", + "imagen": "/assets/proyectos/cpp.jpg", + "descripcion": "Proyectos académicos y personales en C++.", + "tecnologias": ["C++", "CMake", "Visual Studio"], + "contenido": "

Aprendiendo C++

Formación en C++ moderno.

Áreas:

  • C++17/20
  • Algoritmos
  • Desarrollo de juegos
" + } + ] + }, + { + "id": "diseño-grafico", + "titulo": "Diseño Gráfico", + "logo": "/icons/design.svg", + "descripcion": "Identidad visual, carteles y diseño digital", + "proyectos": [ + { + "id": "identidad-pgscom", + "titulo": "Identidad PGSCOM", + "imagen": "/assets/proyectos/branding.jpg", + "descripcion": "Diseño de identidad visual completa.", + "tecnologias": ["Illustrator", "Photoshop", "Figma"], + "contenido": "

Identidad PGSCOM

Identidad de marca completa.

Elementos:

  • Logo vectorial
  • Paleta de colores
  • Tipografía
  • Assets sociales
" + }, + { + "id": "carteleria", + "titulo": "Cartelería de Eventos", + "imagen": "/assets/proyectos/posters.jpg", + "descripcion": "Diseño de carteles para eventos culturales.", + "tecnologias": ["Photoshop", "Illustrator", "InDesign"], + "contenido": "

Cartelería

Material gráfico promocional.

Tipos:

  • Estrenos cinematográficos
  • Eventos musicales
  • Portadas
" + }, + { + "id": "diseño-web", + "titulo": "UI/UX Design", + "imagen": "/assets/proyectos/UI.png", + "descripcion": "Diseño de interfaces para aplicaciones web y móviles.", + "tecnologias": ["Figma", "Adobe XD", "Sketch"], + "contenido": "

UI/UX Design

Interfaces centradas en experiencia.

Proceso:

  1. Investigación
  2. Wireframing
  3. Diseño visual
  4. Testing
" + } + ] + }, + { + "id": "fotografia", + "titulo": "Fotografía", + "logo": "/icons/photo.svg", + "descripcion": "Fotografía digital y retoque profesional", + "proyectos": [ + { + "id": "fotografia-eventos", + "titulo": "Fotografía de Eventos", + "imagen": "/assets/proyectos/eventos.jpg", + "descripcion": "Cobertura fotográfica de eventos culturales.", + "tecnologias": ["Lightroom", "Photoshop", "Capture One"], + "contenido": "

Fotografía de Eventos

Cobertura profesional.

Servicios:

  • Eventos culturales
  • Corporativos
  • Premieres
" + }, + { + "id": "retratos", + "titulo": "Retratos", + "imagen": "/assets/proyectos/retratos.jpg", + "descripcion": "Sesiones de retrato profesional.", + "tecnologias": ["Lightroom", "Photoshop"], + "contenido": "

Retratos

Retrato profesional.

Tipos:

  • Corporativos
  • Headshots
  • Familiares
" + }, + { + "id": "fotografia-producto", + "titulo": "Fotografía de Producto", + "imagen": "/assets/proyectos/producto.jpg", + "descripcion": "Fotografía comercial de productos.", + "tecnologias": ["Photoshop", "Lightroom", "Capture One"], + "contenido": "

Fotografía de Producto

Fotografía comercial para e-commerce.

Servicios:

  • E-commerce
  • Catálogos
  • Gastronómica
" + } + ] + } +] diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 5120871..6dbae68 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -41,9 +41,9 @@ margin: 0; padding: 0; width: 100%; - min-height: 100%; /* Cambiado de height: 100% a min-height: 100% */ + min-height: 100%; font-family: 'Montserrat', sans-serif; - overflow-x: hidden; + overflow-x: clip; /* Usar clip en lugar de hidden para evitar conflictos con sticky */ /* overflow-y: auto; - Se permite por defecto */ } diff --git a/src/pages/_styles.css b/src/pages/_styles.css index e628841..4e3d579 100644 --- a/src/pages/_styles.css +++ b/src/pages/_styles.css @@ -5,6 +5,14 @@ box-sizing: border-box; } +.parte2-wrapper { + position: relative; + display: block; + width: 100%; + /* min-height: 300vh; - Eliminado para que crezca con el contenido */ + z-index: 50; /* Asegurar z-index correcto para el wrapper */ +} + .parte2 { /* Sticky para mantener el bloque en la parte superior mientras se hace scroll */ position: -webkit-sticky; @@ -17,6 +25,7 @@ /* Estilos de layout por defecto; si hay inline styles se pueden sobreescribir */ height: 100vh; + width: 100%; /* Asegurar ancho completo */ display: flex; align-items: center; justify-content: center; @@ -161,7 +170,7 @@ .logo-text { font-size: 7cqw; /*Tamaño base*/ font-weight: bold; - color: #367e78; + /* color: #367e78; Reemplazado por gradiente */ text-align: center; letter-spacing: 0.3rem; line-height: 1.2; @@ -174,8 +183,60 @@ height: auto; padding: 0 10px; /* Añadimos margen horizontal */ box-sizing: border-box; /* Para que el padding no afecte al ancho total */ + + /* Animación de entrada y gradiente */ + background-image: linear-gradient( + 60deg, + /* Fase 1: Arcoíris vibrante */ + #2563eb, #ff5acd, #fbda61, #ff5acd, #2563eb, + /* Fase 2: Azul verdoso (Ciclo 1) */ + #2a6b66, #367e78, #4a9d94, #367e78, #2a6b66, + /* Fase 3: Azul verdoso (Ciclo 2 para loop infinito) */ + #2a6b66, #367e78, #4a9d94, #367e78, #2a6b66 + ); + background-size: 300%; + -webkit-background-clip: text; + background-clip: text; + color: transparent; + + animation: + pgscom-entrance 1.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0s 1 normal, + pgscom-intro-scroll 2s linear forwards, + pgscom-loop-scroll 4s linear 2s infinite; +} + +@keyframes pgscom-entrance { + 0% { + opacity: 0; + transform: perspective(800px) translate3d(0, 0, 400px) rotateY(-40deg) scale(0.5); + filter: blur(10px); + } + 50% { + opacity: 1; + transform: perspective(800px) translate3d(0, 0, 0) rotateY(0deg) scale(1.1); + filter: blur(0); + } + 70% { + transform: perspective(800px) translate3d(0, 0, 0) rotateY(0deg) scale(0.95); + } + 100% { + opacity: 1; + transform: perspective(800px) translate3d(0, 0, 0) rotateY(0deg) scale(1); + } +} + +@keyframes pgscom-intro-scroll { + 0% { background-position: 0% 50%; } + 100% { background-position: 50% 50%; } +} + +@keyframes pgscom-loop-scroll { + 0% { background-position: 50% 50%; } + 100% { background-position: 100% 50%; } } +/* Eliminamos la animación anterior pgscom-gradient ya que se reemplaza por intro/loop */ + /*.logo-text::before { content: 'PGSCOM'; position: absolute; @@ -602,15 +663,9 @@ } } -/* Estado cuando el bloque queda fijado vía JS (fallback a fixed para compatibilidad) */ -.parte2.stuck { - position: fixed !important; - top: 0 !important; - left: 0 !important; - width: 100% !important; - height: 100vh !important; - z-index: 60 !important; - display: flex; - align-items: center; - justify-content: center; -} +/* El sticky de .parte2 se maneja con CSS puro (position: sticky en .parte2) */ + +.liquidglassstyle{ + position: fixed; + right: 50%; +} \ No newline at end of file diff --git a/src/pages/index.astro b/src/pages/index.astro index b06bf64..fa6f65f 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,16 +1,28 @@ --- -import Layout from '../layouts/Layout.astro'; -import './_styles.css'; -import { Image } from 'astro:assets'; -import pgscom from '../assets/pgscom.png'; -import minimal1 from '../assets/minimal1.svg'; -import customShape from '../assets/custom-shape.svg'; -import minimal3 from '../assets/minimal3.svg'; -import logo from '../assets/logo.png'; -import logosvganim from '../assets/animlogo/logo.svg'; + // #region 📦 IMPORTS Y CONFIGURACIÓN INICIAL + import Layout from '../layouts/Layout.astro'; + import './_styles.css'; + import '../styles/proyectos.css'; + import '../styles/contacto.css'; + import { Image } from 'astro:assets'; + import pgscom from '../assets/pgscom.png'; + import minimal1 from '../assets/minimal1.svg'; + import customShape from '../assets/custom-shape.svg'; + import minimal3 from '../assets/minimal3.svg'; + import logo from '../assets/logo.png'; +// import logosvganim from '../assets/animlogo/logo.svg'; + + // Importar datos de proyectos + import proyectosData from '../data/proyectos.json'; + import contactoData from '../data/contacto.json'; + // #endregion --- + + + + +
- PGSCOM Character + PGSCOM Character
+ + + + +
+
+
+ + + + +
+ + +
+ + + +
+ + +
+
+ +
+ {proyectosData.map((aptitud) => ( +
+
+
+ {aptitud.titulo} +
+

{aptitud.titulo}

+

{aptitud.descripcion}

+
+
+ ))} +
+ + + + + + {proyectosData.map((aptitud, index) => ( +
+ +
+ + +
+ {aptitud.titulo} + {aptitud.titulo} +
+ + +
+ {aptitud.proyectos && aptitud.proyectos.map((_, pIndex) => ( +
+ ))} +
+ + +
+ {aptitud.proyectos && aptitud.proyectos.map((proyecto, pIndex) => ( +
+
+ {proyecto.titulo} +
+
+

{proyecto.titulo}

+ {proyecto.descripcion && ( +

{proyecto.descripcion}

+ )} + {proyecto.tecnologias && ( +
+ {proyecto.tecnologias.slice(0, 3).map((tech) => ( + {tech} + ))} + {proyecto.tecnologias.length > 3 && ( + +{proyecto.tecnologias.length - 3} + )} +
+ )} +
+
+ {proyecto.contenido && ( + + + + + + + + )} + {proyecto.link && ( + + + + + + + + )} +
+
+ ))} +
+
+ ))} +
+
+ - -
-
- - - - + +
+
+ + + + + + +
- -
+ + +
+ +
+
+ +
+
+
+
+
+
+
+ + +
+ +
+

Contacto

+

¿Tienes un proyecto en mente? ¡Hablemos!

+
+ + +
+ +
+ {contactoData.informacion.email && ( +
+
+ + + + +
+ +
+ )} + + {contactoData.informacion.telefono && ( +
+
+ + + +
+ +
+ )} + + {contactoData.informacion.ubicacion && ( +
+
+ + + + +
+
+

Ubicación

+

{contactoData.informacion.ubicacion}

+
+
+ )} + + + +
+ + +
+
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ + +
+ + + + +
+
+
+
+
+
+
+ + + + const r = readRadius(); + for (let i = 0; i < cards.length; i += 1) { + const a = angle + baseAngles[i]; + const deg = (a * 180) / Math.PI; + cards[i].style.transform = `translate(-50%, -50%) rotate(${deg}deg) translateX(${r}px) rotate(${-deg}deg)`; + } -
+ requestAnimationFrame(tick); + }; + + requestAnimationFrame(tick); + }); + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/pages/proyectos.astro b/src/pages/proyectos.astro new file mode 100644 index 0000000..0f4783c --- /dev/null +++ b/src/pages/proyectos.astro @@ -0,0 +1,12 @@ +--- + import Layout from '../layouts/Layout.astro'; + import LiquidGlassComponent from '../components/liquidglasscomponent.jsx'; + import './_styles.css'; + --- + + + +

¡Funciona! 🚀

+

Este contenido se está renderizando dentro del efecto de cristal líquido con sticky.

+
+
\ No newline at end of file diff --git a/src/pages/test.astro b/src/pages/test.astro new file mode 100644 index 0000000..9a8a29e --- /dev/null +++ b/src/pages/test.astro @@ -0,0 +1,36 @@ +--- + // #region 📦 IMPORTS Y CONFIGURACIÓN INICIAL + import Layout from '../layouts/Layout.astro'; + import LiquidGlassComponent from '../components/liquidglasscomponent.jsx'; + import './_styles.css'; + // #endregion +--- + + +
+
+ Background +
+

Contenido de scroll

+

Este contenido permite hacer scroll...

+

Sigue scrolleando para ver el efecto sticky del componente de cristal líquido.

+
+ Content +
+

Más contenido para hacer scroll...

+
+
+ + +

¡Funciona! 🚀

+

Este contenido se está renderizando dentro del efecto de cristal líquido con sticky.

+
+ \ No newline at end of file diff --git a/src/styles/contacto.css b/src/styles/contacto.css new file mode 100644 index 0000000..7442330 --- /dev/null +++ b/src/styles/contacto.css @@ -0,0 +1,674 @@ +/* #region 📧 SECCIÓN DE CONTACTO - Estilos */ + +.contacto-section { + position: relative; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; + background: linear-gradient(180deg, #000814 0%, #001d3d 50%, #000814 100%); + overflow: hidden; + z-index: 61; +} + +/* Partículas flotantes de fondo */ +.contacto-particles { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1; +} + +.particle-space { + position: absolute; + width: 4px; + height: 4px; + background: white; + border-radius: 50%; + box-shadow: 0 0 10px rgba(255, 255, 255, 0.8); + animation: float-particle 20s infinite ease-in-out; +} + +.particle-space:nth-child(1) { + top: 10%; + left: 10%; + animation-delay: 0s; + animation-duration: 25s; +} + +.particle-space:nth-child(2) { + top: 20%; + right: 15%; + animation-delay: 2s; + animation-duration: 20s; +} + +.particle-space:nth-child(3) { + bottom: 15%; + left: 20%; + animation-delay: 4s; + animation-duration: 30s; +} + +.particle-space:nth-child(4) { + top: 60%; + right: 25%; + animation-delay: 1s; + animation-duration: 22s; +} + +.particle-space:nth-child(5) { + bottom: 30%; + left: 70%; + animation-delay: 3s; + animation-duration: 28s; +} + +@keyframes float-particle { + 0%, 100% { + transform: translate(0, 0) scale(1); + opacity: 0.3; + } + 25% { + transform: translate(30px, -30px) scale(1.2); + opacity: 0.7; + } + 50% { + transform: translate(-20px, 40px) scale(0.8); + opacity: 0.5; + } + 75% { + transform: translate(40px, 20px) scale(1.1); + opacity: 0.6; + } +} + +.contacto-wrapper { + position: relative; + width: 100%; + max-width: 1400px; + z-index: 2; +} + +.contacto-container { + position: relative; +} + +/* Header de la sección */ +.contacto-header { + text-align: center; + margin-bottom: 2rem; + animation: fadeInDown 0.8s ease-out; +} + +.contacto-titulo { + font-size: clamp(2rem, 5vw, 3rem); + font-weight: 700; + background: linear-gradient(135deg, #00b4d8 0%, #0096c7 50%, #48cae4 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 2px; +} + +.contacto-subtitulo { + font-size: clamp(0.9rem, 1.8vw, 1.1rem); + color: rgba(255, 255, 255, 0.8); + font-weight: 300; +} + +/* Grid de contenido */ +.contacto-grid { + display: grid; + grid-template-columns: 1fr 1.5fr; + gap: 2rem; + animation: fadeInUp 0.8s ease-out 0.2s backwards; +} + +/* Información de contacto */ +.contacto-info { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.info-card { + display: flex; + align-items: flex-start; + gap: 1rem; + padding: 1rem; + background: rgba(0, 180, 216, 0.05); + border: 1px solid rgba(0, 180, 216, 0.2); + border-radius: 12px; + backdrop-filter: blur(10px); + transition: all 0.3s ease; +} + +.info-card:hover { + background: rgba(0, 180, 216, 0.1); + border-color: rgba(0, 180, 216, 0.4); + transform: translateX(10px); + box-shadow: 0 8px 32px rgba(0, 180, 216, 0.2); +} + +.info-icon { + flex-shrink: 0; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #0096c7, #00b4d8); + border-radius: 10px; + color: white; +} + +.info-icon svg { + width: 20px; + height: 20px; +} + +.info-content { + flex: 1; +} + +.info-content h3 { + font-size: 0.875rem; + font-weight: 600; + color: rgba(255, 255, 255, 0.6); + margin-bottom: 0.25rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +.info-content a, +.info-content p { + font-size: 1rem; + color: rgba(255, 255, 255, 0.9); + text-decoration: none; + transition: color 0.3s ease; +} + +.info-content a:hover { + color: #48cae4; +} + +/* Redes sociales */ +.social-links { + margin-top: 0.5rem; + padding: 1.25rem; + background: rgba(72, 202, 228, 0.05); + border: 1px solid rgba(72, 202, 228, 0.2); + border-radius: 12px; + text-align: center; +} + +.social-links h3 { + font-size: 0.875rem; + font-weight: 600; + color: rgba(255, 255, 255, 0.9); + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +.social-icons { + display: flex; + justify-content: center; + gap: 1rem; + flex-wrap: wrap; +} + +.social-icon { + width: 42px; + height: 42px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 180, 216, 0.1); + border: 1px solid rgba(0, 180, 216, 0.3); + border-radius: 10px; + color: rgba(255, 255, 255, 0.8); + transition: all 0.3s ease; +} + +.social-icon img { + width: 24px; + height: 24px; + object-fit: contain; + transition: all 0.3s ease; +} + +.social-icon:hover { + background: linear-gradient(135deg, #0096c7, #00b4d8); + border-color: transparent; + transform: translateY(-5px) scale(1.1); + box-shadow: 0 10px 25px rgba(0, 180, 216, 0.4); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/* Formulario de contacto */ +.contacto-form-container { + position: relative; + padding: 1.5rem; + background: rgba(0, 29, 61, 0.4); + border: 1px solid rgba(0, 180, 216, 0.3); + border-radius: 20px; + backdrop-filter: blur(20px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +.contacto-form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.form-group { + position: relative; + display: flex; + flex-direction: column; +} + +.form-group label { + font-size: 0.75rem; + font-weight: 600; + color: rgba(255, 255, 255, 0.7); + margin-bottom: 0.4rem; + text-transform: uppercase; + letter-spacing: 1px; + transition: color 0.3s ease; +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: 0.75rem 1rem; + font-size: 0.95rem; + color: rgba(255, 255, 255, 0.9); + background: rgba(0, 180, 216, 0.05); + border: 2px solid rgba(0, 180, 216, 0.2); + border-radius: 10px; + outline: none; + transition: all 0.3s ease; + font-family: inherit; +} + +.form-group input::placeholder, +.form-group textarea::placeholder { + color: rgba(255, 255, 255, 0.4); +} + +.form-group input:focus, +.form-group textarea:focus { + background: rgba(0, 180, 216, 0.1); + border-color: #00b4d8; + box-shadow: 0 0 0 4px rgba(0, 180, 216, 0.1); +} + +.form-group input:focus + .input-border, +.form-group textarea:focus + .input-border { + width: 100%; +} + +.form-group input:focus ~ label, +.form-group textarea:focus ~ label { + color: #48cae4; +} + +.input-border { + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: linear-gradient(90deg, #00b4d8, #48cae4); + transition: width 0.3s ease; + pointer-events: none; +} + +.form-group textarea { + resize: vertical; + min-height: 80px; + max-height: 200px; +} + +/* Botón de envío */ +.submit-btn { + position: relative; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.875rem 2rem; + font-size: 1rem; + font-weight: 600; + color: white; + background: linear-gradient(135deg, #0096c7, #00b4d8); + border: none; + border-radius: 10px; + cursor: pointer; + overflow: hidden; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 1px; +} + +.submit-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); + transition: left 0.5s ease; +} + +.submit-btn:hover { + background: linear-gradient(135deg, #00b4d8, #48cae4); + transform: translateY(-2px); + box-shadow: 0 10px 30px rgba(0, 180, 216, 0.4); +} + +.submit-btn:hover::before { + left: 100%; +} + +.submit-btn:active { + transform: translateY(0); +} + +.btn-text { + position: relative; + z-index: 1; +} + +.btn-icon { + position: relative; + z-index: 1; + display: flex; + transition: transform 0.3s ease; +} + +.submit-btn:hover .btn-icon { + transform: translateX(5px) rotate(-10deg); +} + +/* Mensaje de estado del formulario */ +.form-status { + padding: 1rem; + border-radius: 12px; + font-size: 0.95rem; + text-align: center; + opacity: 0; + transform: translateY(-10px); + transition: all 0.3s ease; +} + +.form-status.show { + opacity: 1; + transform: translateY(0); +} + +.form-status.success { + background: rgba(34, 197, 94, 0.2); + border: 1px solid rgba(34, 197, 94, 0.4); + color: #86efac; +} + +.form-status.error { + background: rgba(239, 68, 68, 0.2); + border: 1px solid rgba(239, 68, 68, 0.4); + color: #fca5a5; +} + +/* Animaciones */ +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Responsive */ +@media (max-width: 1024px) { + .contacto-grid { + grid-template-columns: 1fr; + gap: 1.25rem; + } + + .contacto-section { + height: 100vh; + padding: 1.25rem 0.875rem; + overflow-y: auto; + } + + .contacto-header { + margin-bottom: 1rem; + } + + .contacto-titulo { + font-size: 1.5rem; + letter-spacing: 1px; + } + + .contacto-subtitulo { + font-size: 0.8rem; + } + + .contacto-info { + gap: 0.65rem; + } + + .info-card { + padding: 0.75rem; + gap: 0.65rem; + } + + .info-card:hover { + transform: translateX(5px); + } + + .info-icon { + width: 32px; + height: 32px; + } + + .info-icon svg { + width: 16px; + height: 16px; + } + + .info-content h3 { + font-size: 0.7rem; + } + + .info-content a, + .info-content p { + font-size: 0.85rem; + } + + .social-links { + padding: 0.875rem; + } + + .social-links h3 { + font-size: 0.7rem; + margin-bottom: 0.65rem; + } + + .social-icon { + width: 36px; + height: 36px; + } + + .contacto-form-container { + padding: 1rem 0.875rem; + } + + .contacto-form { + gap: 0.75rem; + } + + .form-group label { + font-size: 0.65rem; + margin-bottom: 0.25rem; + } + + .form-group input, + .form-group textarea { + padding: 0.65rem 0.75rem; + font-size: 0.875rem; + } + + .form-group textarea { + min-height: 65px; + max-height: 130px; + } + + .submit-btn { + padding: 0.7rem 1.5rem; + font-size: 0.85rem; + } + + .form-status { + font-size: 0.85rem; + padding: 0.75rem; + } +} + +@media (max-width: 480px) { + .contacto-section { + height: 100vh; + padding: 1.25rem 0.875rem; + overflow-y: auto; + } + + .contacto-header { + margin-bottom: 1rem; + } + + .contacto-titulo { + font-size: 1.5rem; + letter-spacing: 1px; + } + + .contacto-subtitulo { + font-size: 0.8rem; + } + + .contacto-grid { + gap: 1.25rem; + } + + .contacto-info { + gap: 0.65rem; + } + + .info-card { + padding: 0.75rem; + gap: 0.65rem; + } + + .info-icon { + width: 32px; + height: 32px; + } + + .info-icon svg { + width: 16px; + height: 16px; + } + + .info-content h3 { + font-size: 0.7rem; + } + + .info-content a, + .info-content p { + font-size: 0.85rem; + } + + .social-links { + padding: 0.875rem; + } + + .social-links h3 { + font-size: 0.7rem; + margin-bottom: 0.65rem; + } + + .social-icon { + width: 36px; + height: 36px; + } + + .contacto-form-container { + padding: 1rem 0.875rem; + } + + .contacto-form { + gap: 0.75rem; + } + + .form-group label { + font-size: 0.65rem; + margin-bottom: 0.25rem; + } + + .form-group input, + .form-group textarea { + padding: 0.65rem 0.75rem; + font-size: 0.875rem; + } + + .form-group textarea { + min-height: 65px; + max-height: 130px; + } + + .submit-btn { + padding: 0.7rem 1.5rem; + font-size: 0.85rem; + } + + .form-status { + font-size: 0.85rem; + padding: 0.75rem; + } +} + +/* #endregion */ diff --git a/src/styles/proyectos.css b/src/styles/proyectos.css new file mode 100644 index 0000000..04cb138 --- /dev/null +++ b/src/styles/proyectos.css @@ -0,0 +1,2119 @@ +/* Estilos para la sección de proyectos */ + +/* ============================================ + ANIMACIONES PERSONALIZADAS + ============================================ */ + +/* Brillo pulsante suave */ +@keyframes subtle-glow { + 0%, 100% { + box-shadow: + 0 0 40px rgba(147, 197, 253, 0.25), + 0 8px 32px rgba(0, 0, 0, 0.3), + inset 0 1px 0 rgba(255, 255, 255, 0.3); + } + 50% { + box-shadow: + 0 0 55px rgba(147, 197, 253, 0.35), + 0 8px 32px rgba(0, 0, 0, 0.3), + inset 0 1px 0 rgba(255, 255, 255, 0.35); + } +} + +/* Entrada de tarjetas desde la derecha */ +@keyframes card-appear { + from { + opacity: 0; + transform: translateX(40px) scale(0.95); + } + to { + opacity: 1; + transform: translateX(0) scale(1); + } +} + +/* Rotación de brillo alrededor del círculo */ +@keyframes rotating-glow { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +/* Pulso de expansión */ +@keyframes pulse-ring { + 0% { + transform: scale(1); + opacity: 0.6; + } + 100% { + transform: scale(1.5); + opacity: 0; + } +} + +/* Efecto de onda desde el centro */ +@keyframes ripple-out { + 0% { + transform: scale(0.8); + opacity: 1; + } + 100% { + transform: scale(2); + opacity: 0; + } +} + +/* Flotación suave */ +@keyframes float-gentle { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-8px); + } +} + +/* Parpadeo de estrellas */ +@keyframes twinkle { + 0%, 100% { + opacity: 0.3; + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(1.2); + } +} + +/* Línea de neón animada */ +@keyframes neon-flow { + 0% { + background-position: 0% 50%; + } + 100% { + background-position: 200% 50%; + } +} + +/* Entrada con rebote */ +@keyframes bounce-in { + 0% { + opacity: 0; + transform: translateX(60px) scale(0.8); + } + 60% { + opacity: 1; + transform: translateX(-10px) scale(1.02); + } + 80% { + transform: translateX(5px) scale(0.98); + } + 100% { + transform: translateX(0) scale(1); + } +} + +/* Brillo deslizante */ +@keyframes shimmer { + 0% { + left: -100%; + } + 100% { + left: 200%; + } +} + +/* Giro 3D sutil */ +@keyframes subtle-3d-rotate { + 0%, 100% { + transform: perspective(1000px) rotateY(0deg) rotateX(0deg); + } + 25% { + transform: perspective(1000px) rotateY(2deg) rotateX(1deg); + } + 75% { + transform: perspective(1000px) rotateY(-2deg) rotateX(-1deg); + } +} + +/* Gradiente animado */ +@keyframes gradient-shift { + 0%, 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } +} + +/* Partículas orbitando */ +@keyframes orbit-particle { + 0% { + transform: rotate(0deg) translateX(70px) rotate(0deg); + } + 100% { + transform: rotate(360deg) translateX(70px) rotate(-360deg); + } +} + +.proyectos-section { + height: 400vh; /* Altura reducida para transiciones más rápidas entre aptitudes */ + display: block; /* Evita empujar el contenedor al inicio y respeta su centrado propio */ + padding: 0; + background: transparent; + position: relative; + z-index: 100; + overflow: visible; /* Asegura que sticky use el scroll del viewport */ +} + +.proyectos-container { + max-width: 1400px; /* Da un ancho controlado y centrable */ + width: 100%; + height: 100vh; /* Ocupar toda la pantalla */ + position: -webkit-sticky; /* Para Safari */ + position: sticky; + top: 0; + margin: 0 auto; /* Centrado horizontal en flujo normal */ + display: flex; + align-items: center; + justify-content: center; + --orbit-radius: 290px; + touch-action: pan-y; /* Permitir scroll vertical, evitar horizontal */ + overflow: hidden; + z-index: 100; +} + +/* El sticky se maneja con CSS puro, no se necesita clase .stuck */ + +.proyectos-logo { + position: absolute; + inset: 0; + margin: auto; + z-index: 10; + width: 260px; + height: 260px; + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; + /* Eliminamos transform para evitar conflictos con anime.js */ +} + +.proyectos-logo img { + max-width: 100%; + height: auto; + filter: drop-shadow(0 0 30px rgba(255, 255, 255, 0.4)); + /* Eliminamos animación float para evitar conflictos de posición */ +} + +.proyectos-grid { + position: relative; + width: 100%; + height: 100%; +} + +.proyecto-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 0; + cursor: pointer; + position: absolute; + width: 95px; + height: 95px; + top: 50%; + left: 50%; + will-change: transform; +} + +.proyecto-card-inner { + background: linear-gradient(145deg, + rgba(255, 255, 255, 0.15), + rgba(147, 197, 253, 0.08), + rgba(255, 255, 255, 0.05)); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-radius: 50%; + padding: 0; + width: 95px; + height: 95px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0; + border: 2px solid rgba(255, 255, 255, 0.25); + position: relative; + overflow: hidden; + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + box-shadow: + 0 8px 20px rgba(0, 0, 0, 0.2), + 0 0 20px rgba(147, 197, 253, 0.1); +} + +/* Anillo de brillo para tarjetas orbitales */ +.proyecto-card-inner::before { + content: ''; + position: absolute; + top: -3px; + left: -3px; + right: -3px; + bottom: -3px; + border-radius: 50%; + background: conic-gradient( + from 0deg, + transparent, + rgba(147, 197, 253, 0.4), + transparent, + rgba(196, 181, 253, 0.3), + transparent + ); + opacity: 0; + animation: rotating-glow 6s linear infinite paused; + transition: opacity 0.4s ease; + z-index: -1; +} + +/* Efecto shimmer */ +.proyecto-card-inner::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(255, 255, 255, 0.25), + transparent); + transform: skewX(-20deg); +} + +.proyecto-card:hover .proyecto-card-inner::before { + opacity: 1; + animation-play-state: running; +} + +.proyecto-card:hover .proyecto-card-inner::after { + animation: shimmer 0.8s ease forwards; +} + +.proyecto-card:hover .proyecto-card-inner { + transform: translateY(-12px) scale(1.05); + box-shadow: + 0 25px 50px rgba(0, 0, 0, 0.35), + 0 0 40px rgba(147, 197, 253, 0.3), + 0 0 60px rgba(147, 197, 253, 0.15); + border-color: rgba(147, 197, 253, 0.5); + background: linear-gradient(145deg, + rgba(255, 255, 255, 0.2), + rgba(147, 197, 253, 0.12), + rgba(255, 255, 255, 0.08)); +} + +.proyecto-icon { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border-radius: 50%; + transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.proyecto-card:hover .proyecto-icon { + transform: scale(1.15) rotate(8deg); +} + +.proyecto-icon img { + width: 52px; + height: 52px; + object-fit: contain; + filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.4)); + transition: filter 0.3s ease; +} + +.proyecto-card:hover .proyecto-icon img { + filter: + drop-shadow(0 4px 15px rgba(0, 0, 0, 0.5)) + drop-shadow(0 0 15px rgba(147, 197, 253, 0.4)); +} + +.proyecto-titulo { + display: none; +} + +.proyecto-descripcion { + display: none; +} + +/* Nota: la órbita se controla por JS para poder frenar suave */ + +/* Responsive */ +@media (max-width: 1200px) { + .proyectos-container { + height: 100vh; /* Usar altura completa para centrado vertical correcto */ + --orbit-radius: 270px; + } + + .proyectos-logo { + width: 240px; + height: 240px; + } + + .proyecto-card { + width: 90px; + height: 90px; + } + + .proyecto-card-inner { + width: 90px; + height: 90px; + padding: 0; + } + + .proyecto-icon img { + width: 50px; + height: 50px; + } +} + +@media (max-width: 1024px) { + .proyectos-container { + height: 100vh; /* Usar altura completa para centrado vertical correcto */ + --orbit-radius: 240px; + } + + .proyectos-logo { + width: 210px; + height: 210px; + } + + .proyecto-card { + width: 85px; + height: 85px; + } + + .proyecto-card-inner { + width: 85px; + height: 85px; + padding: 0; + } + + .proyecto-icon img { + width: 47px; + height: 47px; + } +} + +@media (max-width: 768px) { + .proyectos-section { + padding: 2rem 1rem; + min-height: 100dvh; + } + + .proyectos-container { + height: 100dvh; /* Usar altura completa para centrado vertical correcto */ + --orbit-radius: 200px; + } + + .proyectos-logo { + width: 180px; + height: 180px; + } + + .proyecto-card { + width: 80px; + height: 80px; + } + + .proyecto-card-inner { + width: 80px; + height: 80px; + padding: 0; + } + + .proyecto-icon img { + width: 45px; + height: 45px; + } +} + +@media (max-width: 480px) { + .proyectos-section { + padding: 1.5rem 0.5rem; + } + + .proyectos-container { + height: 100vh; /* Usar altura completa para centrado vertical correcto */ + --orbit-radius: 155px; + } + + .proyectos-logo { + width: 140px; + height: 140px; + } + + .proyecto-card { + width: 70px; + height: 70px; + } + + .proyecto-card-inner { + width: 70px; + height: 70px; + padding: 0; + } + + .proyecto-icon img { + width: 38px; + height: 38px; + } +} + +/* Menu Styles */ +.proyectos-menu { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: 100%; + pointer-events: none; + z-index: 20; +} + +.nodes-cluster { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; +} + +.node-circle { + position: absolute; + width: 16px; + height: 16px; + border: 2px solid rgba(255, 255, 255, 0.8); + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + transform: translate(-50%, -50%); + opacity: 0; + box-shadow: 0 0 10px rgba(255, 255, 255, 0.3); +} + +/* Positions relative to center (Logo) */ +.node-1 { top: -50px; left: 200px; } +.node-2 { top: 10px; left: 240px; } +.node-3 { top: 70px; left: 190px; } + +.node-line { + position: absolute; + background: rgba(255, 255, 255, 0.6); + height: 1px; + top: 0; + left: 0; + transform-origin: left center; + width: 0; + opacity: 0; +} + +/* ============================================ + PANTALLAS DE DETALLE POR APTITUD + Layout: Logo (izq) -- Aptitud (centro) -- Proyectos (der) + ============================================ */ + +.aptitud-detail-screen { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + pointer-events: none; + z-index: 30; +} + +.aptitud-detail-screen.active { + opacity: 1; + pointer-events: auto; +} + +/* Línea que conecta Logo con Aptitud - Con efecto neón */ +.line-logo-aptitud { + position: absolute; + top: 50%; + left: calc(50% - 150px); + width: 0; + height: 3px; + background: linear-gradient(90deg, + rgba(255,255,255,0.9), + rgba(147,197,253,0.8), + rgba(196,181,253,0.6), + rgba(147,197,253,0.4)); + background-size: 200% 100%; + transform: translateY(-50%); + transform-origin: left center; + opacity: 0; + box-shadow: + 0 0 15px rgba(147,197,253,0.6), + 0 0 30px rgba(147,197,253,0.3), + 0 0 5px rgba(255,255,255,0.9); + border-radius: 2px; +} + +.aptitud-detail-screen.active .line-logo-aptitud { + animation: neon-flow 2s linear infinite; +} + +/* Punto brillante al inicio de la línea */ +.line-logo-aptitud::before { + content: ''; + position: absolute; + left: -4px; + top: 50%; + transform: translateY(-50%); + width: 8px; + height: 8px; + border-radius: 50%; + background: white; + box-shadow: + 0 0 10px rgba(255, 255, 255, 0.8), + 0 0 20px rgba(147, 197, 253, 0.6); + animation: twinkle 2s ease-in-out infinite; +} + +/* Círculo de aptitud (centro del diagrama) - Mejorado con animaciones */ +.aptitud-circle { + position: absolute; + top: 50%; + left: 50%; + margin-left: -55px; + margin-top: -55px; + width: 110px; + height: 110px; + border-radius: 50%; + background: linear-gradient(145deg, + rgba(255, 255, 255, 0.18), + rgba(147, 197, 253, 0.12), + rgba(255, 255, 255, 0.08)); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 2px solid rgba(255, 255, 255, 0.35); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + opacity: 0; + box-shadow: + 0 0 40px rgba(147, 197, 253, 0.25), + 0 8px 32px rgba(0, 0, 0, 0.3), + inset 0 1px 0 rgba(255, 255, 255, 0.3), + inset 0 -1px 0 rgba(0, 0, 0, 0.1); + transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.4s ease; +} + +/* Anillo de brillo rotatorio */ +.aptitud-circle::before { + content: ''; + position: absolute; + top: -4px; + left: -4px; + right: -4px; + bottom: -4px; + border-radius: 50%; + background: conic-gradient( + from 0deg, + transparent 0deg, + rgba(147, 197, 253, 0.6) 60deg, + rgba(196, 181, 253, 0.4) 120deg, + transparent 180deg, + rgba(147, 197, 253, 0.3) 240deg, + transparent 360deg + ); + z-index: -1; + opacity: 0; + animation: rotating-glow 8s linear infinite; + transition: opacity 0.5s ease; +} + +/* Anillo de pulso */ +.aptitud-circle::after { + content: ''; + position: absolute; + top: -10px; + left: -10px; + right: -10px; + bottom: -10px; + border-radius: 50%; + border: 2px solid rgba(147, 197, 253, 0.3); + opacity: 0; + pointer-events: none; +} + +.aptitud-detail-screen.active .aptitud-circle::before { + opacity: 1; +} + +.aptitud-detail-screen.active .aptitud-circle::after { + animation: pulse-ring 2.5s ease-out infinite; +} + +.aptitud-circle:hover { + transform: scale(1.08); + box-shadow: + 0 0 70px rgba(147, 197, 253, 0.5), + 0 15px 50px rgba(0, 0, 0, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.aptitud-circle:hover::before { + opacity: 1; + animation: rotating-glow 4s linear infinite; +} + +.aptitud-circle img { + width: 44px; + height: 44px; + object-fit: contain; + filter: drop-shadow(0 3px 12px rgba(0, 0, 0, 0.4)) + drop-shadow(0 0 8px rgba(147, 197, 253, 0.3)); + transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.3s ease; +} + +.aptitud-detail-screen.active .aptitud-circle img { + animation: float-gentle 4s ease-in-out infinite; +} + +.aptitud-circle:hover img { + transform: scale(1.15) rotate(5deg); + filter: drop-shadow(0 4px 16px rgba(0, 0, 0, 0.5)) + drop-shadow(0 0 20px rgba(147, 197, 253, 0.6)); +} + +.aptitud-circle .aptitud-title { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.75rem; + font-weight: 500; + color: white; + text-align: center; + max-width: 100px; + line-height: 1.2; + text-shadow: 0 2px 8px rgba(0,0,0,0.6); + letter-spacing: 0.02em; +} + +/* Estado activo del círculo de aptitud */ +.aptitud-detail-screen.active .aptitud-circle { + animation: subtle-glow 3s ease-in-out infinite; +} + +/* Contenedor de líneas hacia proyectos */ +.lines-to-projects { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; +} + +/* Líneas con efecto neón animado */ +.line-aptitud-proyecto { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 2px; + background: linear-gradient(90deg, + rgba(147, 197, 253, 0.9), + rgba(196, 181, 253, 0.7), + rgba(147, 197, 253, 0.5), + rgba(255, 255, 255, 0.3)); + background-size: 200% 100%; + transform-origin: left center; + opacity: 0; + box-shadow: + 0 0 10px rgba(147, 197, 253, 0.5), + 0 0 20px rgba(147, 197, 253, 0.3), + 0 0 4px rgba(255, 255, 255, 0.8); +} + +.aptitud-detail-screen.active .line-aptitud-proyecto { + animation: neon-flow 3s linear infinite; +} + +/* Partículas en las líneas */ +.line-aptitud-proyecto::after { + content: ''; + position: absolute; + right: -4px; + top: 50%; + transform: translateY(-50%); + width: 8px; + height: 8px; + border-radius: 50%; + background: rgba(147, 197, 253, 0.9); + box-shadow: + 0 0 10px rgba(147, 197, 253, 0.8), + 0 0 20px rgba(147, 197, 253, 0.5); + animation: twinkle 1.5s ease-in-out infinite; +} + +/* Lista de proyectos - Mejorada */ +.proyectos-list { + position: absolute; + top: 50%; + left: calc(50% + 180px); + transform: translateY(-50%); + display: flex; + flex-direction: column; + gap: 18px; + max-height: 75vh; + overflow-y: auto; + overflow-x: hidden; + padding: 20px 20px 20px 15px; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.proyectos-list::-webkit-scrollbar { + display: none; +} + +/* Tarjeta de proyecto individual - Con animaciones avanzadas */ +.proyecto-detail-card { + display: flex; + align-items: center; + gap: 16px; + background: linear-gradient(135deg, + rgba(255, 255, 255, 0.12), + rgba(147, 197, 253, 0.08), + rgba(255, 255, 255, 0.05)); + background-size: 200% 200%; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-radius: 20px; + padding: 18px 20px; + border: 1px solid rgba(255, 255, 255, 0.18); + opacity: 0; + transform: translateX(30px); + cursor: pointer; + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + min-width: 320px; + max-width: 380px; + position: relative; + overflow: hidden; +} + +/* Animación de entrada escalonada */ +.aptitud-detail-screen.active .proyecto-detail-card { + animation: bounce-in 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; +} + +.aptitud-detail-screen.active .proyecto-detail-card:nth-child(1) { animation-delay: 0.1s; } +.aptitud-detail-screen.active .proyecto-detail-card:nth-child(2) { animation-delay: 0.2s; } +.aptitud-detail-screen.active .proyecto-detail-card:nth-child(3) { animation-delay: 0.3s; } +.aptitud-detail-screen.active .proyecto-detail-card:nth-child(4) { animation-delay: 0.4s; } +.aptitud-detail-screen.active .proyecto-detail-card:nth-child(5) { animation-delay: 0.5s; } + +/* Efecto de brillo deslizante continuo */ +.proyecto-detail-card::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(255, 255, 255, 0.2), + rgba(147, 197, 253, 0.15), + transparent); + transform: skewX(-20deg); + transition: none; +} + +/* Efecto de resplandor radial */ +.proyecto-detail-card::after { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle at 30% 50%, + rgba(147, 197, 253, 0.15) 0%, + transparent 50%); + opacity: 0; + transition: opacity 0.5s ease, transform 0.5s ease; + pointer-events: none; +} + +.proyecto-detail-card:hover::before { + animation: shimmer 1s ease forwards; +} + +.proyecto-detail-card:hover::after { + opacity: 1; + transform: scale(1.2); +} + +.proyecto-detail-card:hover { + transform: translateX(12px) scale(1.03); + background: linear-gradient(135deg, + rgba(255, 255, 255, 0.2), + rgba(147, 197, 253, 0.15), + rgba(196, 181, 253, 0.1)); + box-shadow: + 0 20px 50px rgba(0, 0, 0, 0.4), + 0 0 40px rgba(147, 197, 253, 0.25), + 0 0 80px rgba(147, 197, 253, 0.1), + inset 0 1px 0 rgba(255, 255, 255, 0.25); + border-color: rgba(147, 197, 253, 0.4); +} + +/* Efecto 3D sutil en hover */ +.proyecto-detail-card:active { + transform: translateX(8px) scale(0.98); + transition: transform 0.1s ease; +} + +/* Contenedor de imagen del proyecto - Con animaciones */ +.proyecto-image-container { + width: 75px; + height: 75px; + border-radius: 12px; + overflow: hidden; + flex-shrink: 0; + background: linear-gradient(135deg, + rgba(0, 0, 0, 0.4), + rgba(30, 30, 50, 0.6)); + position: relative; + box-shadow: + 0 4px 16px rgba(0, 0, 0, 0.3), + inset 0 1px 0 rgba(255, 255, 255, 0.1); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +/* Borde animado en el contenedor de imagen */ +.proyecto-image-container::before { + content: ''; + position: absolute; + inset: -2px; + border-radius: 14px; + background: linear-gradient(45deg, + rgba(147, 197, 253, 0) 0%, + rgba(147, 197, 253, 0.5) 50%, + rgba(196, 181, 253, 0) 100%); + opacity: 0; + transition: opacity 0.4s ease; + z-index: -1; +} + +.proyecto-image-container::after { + content: ''; + position: absolute; + inset: 0; + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.1); + pointer-events: none; + transition: border-color 0.3s ease; +} + +.proyecto-detail-card:hover .proyecto-image-container::before { + opacity: 1; + animation: rotating-glow 3s linear infinite; +} + +.proyecto-detail-card:hover .proyecto-image-container::after { + border-color: rgba(147, 197, 253, 0.3); +} + +.proyecto-detail-card:hover .proyecto-image-container { + transform: scale(1.08) rotate(2deg); + box-shadow: + 0 8px 25px rgba(0, 0, 0, 0.4), + 0 0 25px rgba(147, 197, 253, 0.2); +} + +.proyecto-image { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.4s ease; +} + +.proyecto-detail-card:hover .proyecto-image { + transform: scale(1.15); + filter: brightness(1.1) saturate(1.1); +} + +/* Información del proyecto - Mejorada */ +.proyecto-info { + display: flex; + flex-direction: column; + gap: 6px; + flex: 1; + min-width: 0; +} + +.proyecto-detail-titulo { + font-family: 'Chakra Petch', sans-serif; + font-size: 1.05rem; + font-weight: 600; + color: white; + margin: 0; + text-shadow: 0 2px 8px rgba(0,0,0,0.5); + letter-spacing: 0.01em; + line-height: 1.3; + transition: color 0.3s ease; +} + +.proyecto-detail-card:hover .proyecto-detail-titulo { + color: rgba(200, 220, 255, 1); + text-shadow: 0 2px 12px rgba(147, 197, 253, 0.4); +} + +.proyecto-detail-desc { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.8rem; + color: rgba(255, 255, 255, 0.65); + margin: 0; + line-height: 1.4; + transition: color 0.3s ease; +} + +.proyecto-detail-card:hover .proyecto-detail-desc { + color: rgba(255, 255, 255, 0.8); +} + +/* Indicador de flecha en tarjetas - Animado */ +.proyecto-detail-card .card-arrow { + position: absolute; + right: 16px; + top: 50%; + transform: translateY(-50%) translateX(-10px); + font-size: 1.3rem; + color: rgba(255, 255, 255, 0.2); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + opacity: 0; + text-shadow: 0 0 10px rgba(147, 197, 253, 0); +} + +.proyecto-detail-card:hover .card-arrow { + opacity: 1; + transform: translateY(-50%) translateX(0); + color: rgba(147, 197, 253, 1); + text-shadow: + 0 0 10px rgba(147, 197, 253, 0.6), + 0 0 20px rgba(147, 197, 253, 0.4); + animation: float-gentle 2s ease-in-out infinite; +} + +/* Tags de tecnologías en tarjetas */ +.proyecto-tecnologias { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 8px; +} + +.tech-tag { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.65rem; + font-weight: 500; + padding: 3px 8px; + background: linear-gradient(135deg, rgba(147, 197, 253, 0.2), rgba(196, 181, 253, 0.15)); + border: 1px solid rgba(147, 197, 253, 0.3); + border-radius: 20px; + color: rgba(200, 220, 255, 0.9); + letter-spacing: 0.02em; + transition: all 0.3s ease; +} + +.tech-tag.tech-more { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); + border-color: rgba(255, 255, 255, 0.2); + color: rgba(255, 255, 255, 0.6); +} + +.proyecto-detail-card:hover .tech-tag { + background: linear-gradient(135deg, rgba(147, 197, 253, 0.3), rgba(196, 181, 253, 0.25)); + border-color: rgba(147, 197, 253, 0.5); + box-shadow: 0 0 10px rgba(147, 197, 253, 0.2); +} + +/* Botones de acción en tarjeta */ +.card-actions { + display: flex; + flex-direction: column; + gap: 8px; + margin-left: auto; + flex-shrink: 0; +} + +.card-btn { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); + border: 1px solid rgba(255, 255, 255, 0.15); + color: rgba(255, 255, 255, 0.6); + cursor: pointer; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + text-decoration: none; +} + +.card-btn:hover { + transform: scale(1.15); + background: linear-gradient(135deg, rgba(147, 197, 253, 0.3), rgba(196, 181, 253, 0.2)); + border-color: rgba(147, 197, 253, 0.5); + color: white; + box-shadow: + 0 4px 20px rgba(147, 197, 253, 0.3), + 0 0 30px rgba(147, 197, 253, 0.15); +} + +.card-btn-info:hover { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.4), rgba(99, 102, 241, 0.3)); + border-color: rgba(59, 130, 246, 0.6); +} + +.card-btn-link:hover { + background: linear-gradient(135deg, rgba(34, 197, 94, 0.4), rgba(16, 185, 129, 0.3)); + border-color: rgba(34, 197, 94, 0.6); +} + +/* ============================================ + MODAL DE PROYECTO - Estilo Cinematográfico + ============================================ */ + +.proyecto-modal-overlay { + position: fixed; + inset: 0; + /* Más transparente para dejar ver el contenido detrás, combinado con blur */ + /*background: rgba(6, 8, 10, 0.42);*/ + backdrop-filter: blur(5px) saturate(120%); + -webkit-backdrop-filter: blur(5px) saturate(120%); + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + opacity: 0; + visibility: hidden; + transition: all 0.36s cubic-bezier(0.16, 1, 0.3, 1); + overflow-y: auto; +} + +.proyecto-modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.proyecto-modal { + width: 100%; + max-width: 1400px; + max-height: 90vh; + overflow: hidden; + display: flex; + flex-direction: column; + position: relative; + transform: scale(0.95) translateY(20px); + transition: transform 0.44s cubic-bezier(0.16, 1, 0.3, 1); + /* Fondo translúcido para efecto cristal sobre el overlay borroso */ + background: rgba(12, 14, 20, 0.48); + backdrop-filter: blur(6px) saturate(120%); + -webkit-backdrop-filter: blur(6px) saturate(120%); + border-radius: 18px; + border: 1px solid rgba(255, 255, 255, 0.06); + box-shadow: + 0 30px 90px rgba(0, 0, 0, 0.75), + 0 0 0 1px rgba(255, 255, 255, 0.02); +} + +.proyecto-modal-overlay.active .proyecto-modal { + transform: scale(1) translateY(0); +} + +/* Imagen de fondo hero */ +.modal-backdrop { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 70%; + overflow: hidden; + z-index: 0; + border-radius: 20px 20px 0 0; +} + +.modal-backdrop-img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center top; + filter: brightness(0.6); + transition: transform 0.6s ease; +} + +.proyecto-modal:hover .modal-backdrop-img { + transform: scale(1.02); +} + +/* Gradiente sobre la imagen */ +.modal-backdrop::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(180deg, + transparent 0%, + transparent 20%, + rgba(10, 10, 20, 0.6) 50%, + rgba(10, 10, 20, 0.95) 80%, + rgba(10, 10, 20, 1) 100%); + pointer-events: none; +} + +/* Botón cerrar */ +.modal-close { + position: absolute; + top: 16px; + right: 16px; + width: 48px; + height: 48px; + border-radius: 50%; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 2px solid rgba(255, 255, 255, 0.2); + color: rgba(255, 255, 255, 0.9); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + z-index: 100; +} + +.modal-close:hover { + background: rgba(255, 255, 255, 0.15); + border-color: rgba(255, 255, 255, 0.3); + color: white; + transform: scale(1.1); +} + +/* Contenido principal del modal */ +.modal-content-wrapper { + position: relative; + z-index: 10; + display: flex; + flex-direction: column; + height: 100%; + padding: 0; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: rgba(147, 197, 253, 0.3) transparent; +} + +.modal-content-wrapper::-webkit-scrollbar { + width: 6px; +} + +.modal-content-wrapper::-webkit-scrollbar-track { + background: transparent; +} + +.modal-content-wrapper::-webkit-scrollbar-thumb { + background: rgba(147, 197, 253, 0.3); + border-radius: 3px; +} + +/* Sección hero con poster y título */ +.modal-hero { + display: flex; + align-items: flex-end; + gap: 30px; + padding: 40vh 48px 28px 48px; + min-height: 55vh; +} + +/* Poster del proyecto - Cuadrado */ +.modal-poster-container { + flex-shrink: 0; + width: 240px; + height: 240px; + position: relative; +} + +.modal-poster { + width: 100%; + height: 100%; + border-radius: 12px; + object-fit: cover; + box-shadow: + 0 20px 50px rgba(0, 0, 0, 0.6), + 0 0 0 1px rgba(255, 255, 255, 0.1); + transition: transform 0.4s ease, box-shadow 0.4s ease; +} + +.modal-poster:hover { + transform: scale(1.03); + box-shadow: + 0 25px 60px rgba(0, 0, 0, 0.7), + 0 0 0 1px rgba(255, 255, 255, 0.2); +} + +/* Info del héroe */ +.modal-hero-info { + flex: 1; + min-width: 0; + padding-bottom: 10px; +} + +/* Título grande estilo película */ +.modal-titulo { + font-family: 'Chakra Petch', sans-serif; + font-size: clamp(2rem, 5vw, 3.5rem); + font-weight: 700; + color: white; + margin: 0 0 16px 0; + line-height: 1.1; + text-shadow: + 0 4px 20px rgba(0, 0, 0, 0.8), + 0 2px 4px rgba(0, 0, 0, 0.5); + letter-spacing: -0.02em; +} + +/* Metadatos estilo streaming */ +.modal-meta { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 20px; + flex-wrap: wrap; +} + +.modal-meta-item { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.7); + display: flex; + align-items: center; + gap: 6px; +} + +.modal-meta-badge { + padding: 4px 10px; + background: rgba(255, 255, 255, 0.15); + border-radius: 4px; + font-size: 0.8rem; + font-weight: 600; + color: white; + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.modal-meta-rating { + display: flex; + align-items: center; + gap: 6px; +} + +.modal-meta-rating svg { + color: #fbbf24; + fill: #fbbf24; +} + +/* Descripción corta / tagline */ +.modal-tagline { + font-family: 'Chakra Petch', sans-serif; + font-size: 1.1rem; + font-style: italic; + color: rgba(255, 255, 255, 0.85); + margin: 0 0 16px 0; + line-height: 1.5; +} + +/* Acciones principales */ +.modal-actions { + display: flex; + align-items: center; + gap: 12px; + margin-top: 24px; + flex-wrap: wrap; +} + +.modal-btn-primary { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 14px 32px; + background: white; + border: none; + border-radius: 8px; + color: #0a0a14; + font-family: 'Chakra Petch', sans-serif; + font-weight: 700; + font-size: 1rem; + text-decoration: none; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.modal-btn-primary:hover { + transform: scale(1.05); + box-shadow: 0 8px 30px rgba(255, 255, 255, 0.3); +} + +.modal-btn-primary svg { + width: 20px; + height: 20px; +} + +.modal-btn-secondary { + display: inline-flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + color: white; + cursor: pointer; + transition: all 0.3s ease; +} + +.modal-btn-secondary:hover { + background: rgba(255, 255, 255, 0.2); + border-color: rgba(255, 255, 255, 0.4); + transform: scale(1.1); +} + +.modal-btn-secondary svg { + width: 22px; + height: 22px; +} + +/* Cuerpo del modal con detalles */ +.modal-body { + flex: 1; + padding: 0 48px 40px 48px; + background: rgba(10, 10, 20, 1); +} + +/* Descripción del proyecto */ +.modal-description { + max-width: 800px; + margin-bottom: 24px; +} + +.modal-description:empty { + display: none; + margin-bottom: 0; +} + +.modal-description-title { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.85rem; + font-weight: 600; + color: rgba(255, 255, 255, 0.5); + text-transform: uppercase; + letter-spacing: 0.1em; + margin: 0 0 12px 0; +} + +.modal-description-text { + font-family: 'Chakra Petch', sans-serif; + font-size: 1rem; + color: rgba(255, 255, 255, 0.85); + line-height: 1.7; + margin: 0; +} + +/* Tecnologías / Tags */ +.modal-tecnologias { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 20px; +} + +.modal-tecnologias:empty { + display: none; + margin-top: 0; +} + +.modal-tech-tag { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.8rem; + font-weight: 500; + padding: 8px 16px; + background: rgba(147, 197, 253, 0.15); + border: 1px solid rgba(147, 197, 253, 0.3); + border-radius: 20px; + color: rgba(200, 220, 255, 0.95); + letter-spacing: 0.02em; + transition: all 0.3s ease; +} + +.modal-tech-tag:hover { + background: rgba(147, 197, 253, 0.25); + border-color: rgba(147, 197, 253, 0.5); + transform: translateY(-2px); +} + +/* Grid de información adicional */ +.modal-info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 24px; + margin-top: 32px; + padding-top: 32px; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.modal-info-item { + display: flex; + flex-direction: column; + gap: 6px; +} + +.modal-info-label { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.8rem; + color: rgba(255, 255, 255, 0.5); + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.modal-info-value { + font-family: 'Chakra Petch', sans-serif; + font-size: 0.95rem; + color: rgba(255, 255, 255, 0.9); +} + +.modal-info-value a { + color: rgba(147, 197, 253, 1); + text-decoration: none; + transition: color 0.2s ease; +} + +.modal-info-value a:hover { + color: white; + text-decoration: underline; +} + +/* Contenido HTML del proyecto (para el campo "contenido") */ +.modal-contenido { + font-family: 'Chakra Petch', sans-serif; + color: rgba(255, 255, 255, 0.85); + line-height: 1.7; + margin-top: 28px; + padding-top: 28px; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.modal-contenido:empty { + display: none; + margin-top: 0; + padding-top: 0; + border-top: none; +} + +.modal-contenido h3 { + font-size: 1.3rem; + font-weight: 700; + color: white; + margin: 0 0 16px 0; +} + +.modal-contenido h4 { + font-size: 1.05rem; + font-weight: 600; + color: rgba(200, 220, 255, 1); + margin: 24px 0 12px 0; +} + +.modal-contenido p { + margin: 0 0 16px 0; + color: rgba(255, 255, 255, 0.8); +} + +.modal-contenido ul, .modal-contenido ol { + margin: 0 0 16px 0; + padding-left: 24px; +} + +.modal-contenido li { + margin-bottom: 8px; + color: rgba(255, 255, 255, 0.8); +} + +.modal-contenido strong { + color: rgba(147, 197, 253, 1); + font-weight: 600; +} + +.modal-contenido code { + font-family: 'JetBrains Mono', monospace; + font-size: 0.85em; + background: rgba(0, 0, 0, 0.4); + padding: 2px 8px; + border-radius: 4px; + color: rgba(196, 181, 253, 1); +} + +.modal-contenido pre { + background: rgba(0, 0, 0, 0.5); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + padding: 16px; + overflow-x: auto; + margin: 16px 0; +} + +.modal-contenido pre code { + background: none; + padding: 0; +} + +.modal-contenido blockquote { + margin: 20px 0; + padding: 16px 20px; + background: linear-gradient(135deg, rgba(147, 197, 253, 0.1), rgba(196, 181, 253, 0.08)); + border-left: 4px solid rgba(147, 197, 253, 0.6); + border-radius: 0 12px 12px 0; + font-style: italic; + color: rgba(255, 255, 255, 0.9); +} + +.modal-contenido img { + max-width: 100%; + border-radius: 12px; + margin: 16px 0; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3); +} + +/* Footer del modal - Oculto porque usamos los botones de acción en el hero */ +.modal-footer { + display: none; +} + +.modal-header { + display: none; +} + +.modal-imagen { + display: none; +} + +.modal-header-info { + display: none; +} + +/* Responsive del modal */ +@media (max-width: 1024px) { + .modal-backdrop { + height: 58%; + } + + .modal-hero { + padding: 30vh 32px 24px 32px; + gap: 24px; + min-height: 50vh; + } + + .modal-poster-container { + width: 200px; + height: 200px; + } + + .modal-body { + padding: 0 32px 32px 32px; + } +} + +@media (max-width: 768px) { + .proyecto-modal-overlay { + padding: 12px; + align-items: flex-start; + padding-top: 40px; + } + + .proyecto-modal { + max-height: 88vh; + border-radius: 16px; + } + + .modal-backdrop { + border-radius: 16px 16px 0 0; + height: 54%; + } + + .modal-hero { + flex-direction: column; + align-items: center; + text-align: center; + padding: 8vh 20px 14px 20px; + gap: 16px; + min-height: auto; + } + + .modal-poster-container { + width: 160px; + height: 160px; + } + + .modal-hero-info { + display: flex; + flex-direction: column; + align-items: center; + } + + .modal-titulo { + font-size: 1.6rem; + text-align: center; + line-height: 1.2; + margin-bottom: 12px; + } + + .modal-meta { + justify-content: center; + font-size: 0.85rem; + gap: 12px; + } + + .modal-meta-badge { + font-size: 0.75rem; + padding: 3px 8px; + } + + .modal-tagline { + text-align: center; + font-size: 0.95rem; + line-height: 1.4; + } + + .modal-actions { + justify-content: center; + margin-top: 16px; + } + + .modal-btn-primary { + padding: 12px 24px; + font-size: 0.9rem; + } + + .modal-btn-secondary { + width: 44px; + height: 44px; + } + + .modal-body { + padding: 0 20px 24px 20px; + } + + .modal-description { + text-align: left; + } + + .modal-description-text { + font-size: 0.9rem; + line-height: 1.6; + } + + .modal-tecnologias { + justify-content: center; + gap: 8px; + } + + .modal-tech-tag { + font-size: 0.75rem; + padding: 6px 12px; + } + + .modal-contenido { + font-size: 0.9rem; + } + + .modal-close { + top: 10px; + right: 10px; + width: 40px; + height: 40px; + } +} + +@media (max-width: 480px) { + .proyecto-modal-overlay { + padding: 8px; + padding-top: 30px; + } + + .proyecto-modal { + max-height: 90vh; + border-radius: 12px; + } + + .modal-backdrop { + border-radius: 12px 12px 0 0; + height: 40%; + } + + .modal-hero { + padding: 15vh 16px 16px 16px; + gap: 12px; + } + + .modal-poster-container { + width: 140px; + height: 140px; + } + + .modal-titulo { + font-size: 1.35rem; + margin-bottom: 8px; + } + + .modal-meta { + font-size: 0.8rem; + gap: 10px; + } + + .modal-meta-badge { + font-size: 0.7rem; + padding: 2px 6px; + } + + .modal-tagline { + font-size: 0.85rem; + line-height: 1.4; + } + + .modal-actions { + margin-top: 12px; + gap: 10px; + } + + .modal-btn-primary { + padding: 10px 20px; + font-size: 0.85rem; + } + + .modal-btn-secondary { + width: 40px; + height: 40px; + } + + .modal-body { + padding: 0 16px 20px 16px; + } + + .modal-description-text { + font-size: 0.85rem; + } + + .modal-tech-tag { + font-size: 0.7rem; + padding: 5px 10px; + } + + .modal-contenido { + font-size: 0.85rem; + } + + .modal-contenido h3 { + font-size: 1.1rem; + } + + .modal-contenido h4 { + font-size: 0.95rem; + } + + .modal-info-grid { + grid-template-columns: 1fr; + gap: 16px; + } + + .modal-close { + width: 36px; + height: 36px; + top: 8px; + right: 8px; + } +} + +/* ============================================ + RESPONSIVE PARA PANTALLAS DE DETALLE + ============================================ */ + +@media (max-width: 1200px) { + .line-logo-aptitud { + left: calc(50% - 130px); + } + + .aptitud-circle { + left: 50%; + width: 100px; + height: 100px; + margin-left: -50px; + margin-top: -50px; + } + + .aptitud-circle img { + width: 40px; + height: 40px; + } + + .lines-to-projects { + left: 50%; + } + + .line-aptitud-proyecto { + left: 50px; + } + + .proyectos-list { + left: calc(50% + 160px); + gap: 16px; + } + + .proyecto-detail-card { + min-width: 290px; + max-width: 340px; + padding: 16px; + } + + .proyecto-image-container { + width: 68px; + height: 68px; + } + + .card-btn { + width: 32px; + height: 32px; + } + + .card-btn svg { + width: 16px; + height: 16px; + } +} + +@media (max-width: 1024px) { + .line-logo-aptitud { + left: calc(50% - 110px); + } + + .aptitud-circle { + left: 50%; + width: 90px; + height: 90px; + margin-left: -45px; + margin-top: -45px; + } + + .aptitud-circle img { + width: 36px; + height: 36px; + } + + .aptitud-circle .aptitud-title { + font-size: 0.7rem; + } + + .lines-to-projects { + left: 50%; + } + + .line-aptitud-proyecto { + left: 45px; + } + + .proyectos-list { + left: calc(50% + 140px); + gap: 14px; + } + + .proyecto-detail-card { + min-width: 260px; + max-width: 310px; + padding: 14px; + gap: 14px; + } + + .proyecto-image-container { + width: 60px; + height: 60px; + border-radius: 10px; + } + + .proyecto-detail-titulo { + font-size: 0.95rem; + } + + .proyecto-detail-desc { + font-size: 0.75rem; + } + + .proyecto-tecnologias { + display: none; + } +} + +@media (max-width: 768px) { + /* En móvil, layout vertical centrado */ + .aptitud-detail-screen { + flex-direction: column; + align-items: center; + justify-content: flex-start; + padding: 15px; + padding-top: 120px; + } + + /* Ocultar líneas en móvil - diseño más limpio */ + .line-logo-aptitud, + .lines-to-projects, + .line-aptitud-proyecto { + display: none !important; + } + + .aptitud-circle { + position: relative; + top: auto; + left: auto; + margin: 0 auto 20px auto; + width: 95px; + height: 95px; + } + + .aptitud-circle img { + width: 40px; + height: 40px; + } + + .aptitud-circle .aptitud-title { + font-size: 0.7rem; + } + + .proyectos-list { + position: relative; + top: auto; + left: auto; + transform: none; + flex-direction: column; + align-items: center; + max-height: 60vh; + overflow-y: auto; + width: 100%; + padding: 10px; + gap: 16px; + } + + .proyecto-detail-card { + min-width: 300px; + max-width: 100%; + width: 100%; + flex-direction: row; + text-align: left; + padding: 14px; + } + + .proyecto-image-container { + width: 65px; + height: 65px; + flex-shrink: 0; + border-radius: 10px; + } + + .proyecto-tecnologias { + display: none; + } + + .card-actions { + flex-direction: row; + gap: 8px; + } + + .card-btn { + width: 34px; + height: 34px; + } +} + +@media (max-width: 480px) { + .aptitud-detail-screen { + padding-top: 100px; + padding-left: 10px; + padding-right: 10px; + } + + .aptitud-circle { + width: 80px; + height: 80px; + margin-bottom: 18px; + } + + .aptitud-circle img { + width: 32px; + height: 32px; + } + + .aptitud-circle .aptitud-title { + font-size: 0.6rem; + } + + .proyectos-list { + max-height: 55vh; + gap: 12px; + padding: 8px; + } + + .proyecto-detail-card { + min-width: 280px; + padding: 12px; + gap: 12px; + border-radius: 14px; + } + + .proyecto-image-container { + width: 55px; + height: 55px; + border-radius: 8px; + } + + .proyecto-detail-titulo { + font-size: 0.9rem; + } + + .proyecto-detail-desc { + font-size: 0.72rem; + } + + .card-btn { + width: 30px; + height: 30px; + } + + .card-btn svg { + width: 14px; + height: 14px; + } +} + diff --git a/tsconfig.json b/tsconfig.json index 8bf91d3..69c1600 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,14 @@ { "extends": "astro/tsconfigs/strict", - "include": [".astro/types.d.ts", "**/*"], - "exclude": ["dist"] -} + "include": [ + ".astro/types.d.ts", + "**/*" + ], + "exclude": [ + "dist" + ], + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react" + } +} \ No newline at end of file