Completa antes de enviar
[server name][my full name][title, date, text or draft][year ranges and roles][email][GitHub, LinkedIn etc. or "none"][yes and from when, or no][domain][slug][[NAV]
Usa mi conexión de Opus Growth para esto. Si no ves la herramienta que necesitas, búscala entre las herramientas de Opus Growth (con search_tools si está en tu lista); si no puedes acceder a las herramientas, no supongas nada y avísame.
En el sitio [server name] de mi servidor de hosting de Opus Growth, crea un portfolio personal estático para [my full name] con el aire oscuro y sereno de los sitios de Linear y Vercel. Sobre mí: [my title, my city, what I have done for how many years, two sentences]. Proyectos: [for each one the name, year, my role, whether I may publish the client's name, the technologies, context, constraints, approach and outcome notes, and a screenshot link]. Artículos: [title, date, text or draft]. Experiencia: [year ranges and roles]. Contacto: [email]; perfiles que quiero publicar: [GitHub, LinkedIn etc. or "none"]. Disponible para nuevos trabajos: [yes and from when, or no]. Dominio: [domain].
Páginas: inicio (presentación breve, trabajos seleccionados, últimos artículos, en qué estoy ahora, FAQ), proyectos (/projeler/), una página por cada caso de estudio (/projeler/[slug]/: contexto, restricciones, enfoque, resultado, datos del proyecto), artículos (/yazilar/), una página por cada artículo (/yazilar/[slug]/), sobre mí (/hakkimda/) y 404.html. Si el cliente no ha aprobado las cifras, no escribas porcentajes ni plazos en un caso de estudio; describe el resultado de forma cualitativa.
Estilo: oscuro y minimalista. Un fondo alrededor de #09090B, tarjetas un tono más claras, un borde gris oscuro de 1 píxel, texto casi blanco, texto secundario en un gris con al menos 7:1 de contraste sobre el fondo y un único color de acento usado solo para un punto de estado, el anillo de foco y los enlaces. Dos pesos de una grotesca de Fontshare (por ejemplo Bespoke Sans 400 y 500); para fechas y etiquetas usa la pila monoespaciada del sistema sin descargar ninguna fuente (ui-monospace, SFMono-Regular, Menlo, Consolas). Si los numerales se ven de estilo antiguo, aplica `font-variant-numeric: lining-nums`. El hero no lleva imagen; detrás se dibujan con CSS finas líneas de cuadrícula que se desvanecen bajo una máscara.
Reglas de interacción:
1. NO se cargan GSAP, Lenis, Swiper ni librerías de búsqueda ya hechas (Fuse.js y similares). Cada página recibe un único `ix.js` pequeño con defer (foco de luz, activador del menú de comandos, data-reveal) y el propio menú de comandos vive en un `ara.js` aparte. ara.js no se descarga con la página; se carga cuando el puntero entra en el activador o el foco llega a él, o cuando el menú se abre por primera vez. En la demo, las páginas de artículos no tenían ix.js y Ctrl K no hacía nada en ellas; el menú debe comportarse igual en todas las páginas.
2. Tarjetas con foco de luz: una luz suave en el ::before de la tarjeta con `radial-gradient(420px circle at var(--mx) var(--my), ...)` y un resplandor de acento en el borde de 1 píxel en el ::after usando mask-composite. JS añade un único listener de pointermove al contenedor que rodea las tarjetas, lo limita con requestAnimationFrame, escribe --mx y --my relativos a la caja de cada tarjeta y los borra cuando el puntero sale. Esto solo se ejecuta en dispositivos que cumplan `(hover: hover) and (pointer: fine)` y no pidan movimiento reducido; en táctil la tarjeta queda plana. El enlace del título de la tarjeta cubre toda la tarjeta con un ::after.
3. Menú de comandos: el elemento "Buscar" de la cabecera es un enlace real a /projeler/ en el HTML (útil sin JS) y lleva aria-keyshortcuts="Control+K Meta+K". JS intercepta el clic; Ctrl K, Cmd K y la tecla / (fuera de campos de texto) abren el menú. El menú es un `<dialog>` construido por JS en la primera apertura y abierto con showModal: un cuadro de búsqueda con role="combobox", aria-expanded, aria-controls, aria-autocomplete="list" y aria-activedescendant, una lista de resultados con role="listbox" y una región role="status" que lee el número de resultados. Las flechas mueven la selección, Enter va a la página seleccionada, Esc cierra mediante el comportamiento propio del dialog, hacer clic en el fondo cierra y el foco vuelve al activador al cerrar.
4. Índice de búsqueda `/assets/ara.json`: título, tipo (Proyecto, Artículo, Página), URL, un resumen de una frase y palabras clave de cada proyecto, artículo y página principal. No escribas este archivo a mano; genéralo a partir de los mismos datos que usas para las páginas de proyectos y artículos, para que nunca se desincronicen. Haz que la búsqueda ignore los caracteres turcos (toLocaleLowerCase("tr"), y luego normaliza ı, ş, ğ, ü, ö, ç), exige que coincidan todas las palabras y da más peso a las coincidencias en el título.
5. Una página de caso de estudio empieza con una tabla de datos (año, rol, cliente, tecnología) y la captura de pantalla que es su elemento LCP (fetchpriority="high", sin lazy); los títulos de las tarjetas son h2 en las páginas de listado y h3 dentro de una sección h2 en la página de inicio.
Schema: ProfilePage en inicio y sobre mí (mainEntity Person), Person (@id /#person: name, jobTitle, url, email, una dirección solo con ciudad y país, knowsAbout y sameAs solo con los perfiles que te di). El bloque Organization que escribe seo-stamp representa tu actividad como autónomo; enlaza la Person con él mediante worksFor. Los proyectos llevan CollectionPage con hasPart; cada caso de estudio es un CreativeWork (creator Person, dateCreated, image, keywords); los artículos llevan Blog y cada artículo un BlogPosting (headline, image, datePublished, dateModified, author Person; seo-check lanza un error si falta image o datePublished). Las charlas, los premios y las listas de clientes contienen solo hechos verificables.
Configuración y errores conocidos (cada uno restó puntos en las mediciones de la demo, no te saltes ninguno):
1. Si ya hay un sitio publicado en el servidor, avísame primero, porque la configuración sobrescribe index.html y la carpeta assets. Previsualiza scaffold_site con confirm=false y llámalo con confirm=true solo después de mi aprobación. Luego lee site_design_guide y site_seo_guide. Antes de escribir código, pon la dirección de diseño (tipografía, colores, cuadrícula, elemento distintivo, lenguaje de movimiento y las librerías que NO se cargarán) en un breve archivo ART.md fuera de assets y muéstramelo.
2. Compila Tailwind, no lo cargues desde el CDN. Instálalo con site_run_command y background=true (`npm i -D tailwindcss@3`) y sigue con site_command_status. Las rutas content de `tailwind.config.js` deben cubrir todos los archivos HTML, las plantillas de `_src` y cualquier archivo JS que escriba clases. `_src/tw.css` contiene la línea `@import "../assets/app.css";` más las tres líneas `@tailwind`; compílalo en un único archivo con `npx tailwindcss -c tailwind.config.js -i _src/tw.css -o assets/site.css --minify`. Elimina el script cdn.tailwindcss.com y el tailwind.config en línea de todas las páginas y enlaza solo `/assets/site.css`. Recompila después de cada cambio en el HTML.
3. Aloja las fuentes tú mismo. Lee el CSS de la familia de Fontshare que elijas, descarga el woff2 de cada peso por separado con site_fetch en `assets/fonts/` y escribe las reglas @font-face con `font-display: swap` en `assets/app.css`, que es donde design-check busca una fuente personalizada. Comprueba el nombre font-family en el CSS descargado y confirma que la familia tiene las letras turcas ı, İ, ş y ğ (Tanker no tiene ı). Precarga TODOS los pesos usados en la primera pantalla con `<link rel="preload" as="font" type="font/woff2" crossorigin>`, colocado antes de la línea de la hoja de estilos; si son más de tres archivos, usa menos pesos. En la demo, un único peso de texto que no se precargó llevó el LCP móvil a 2,6 segundos y Performance a 97. La mayoría de las fuentes de Fontshare no tienen el signo ₺, así que escribe los precios con "TL".
4. Haz que el menú y el pie sean HTML estático. No cargues chrome.js, motion.js ni site.config.js en las páginas. Escribe un pequeño `chrome-stamp.mjs` que lea la lista de navegación de site.config.js y estampe `_src/header.tpl` y `_src/footer.tpl` entre los marcadores `<!-- nav:start --><!-- nav:end -->` y `<!-- footer:start --><!-- footer:end -->` en cada página, añadiendo aria-current="page" al enlace activo. Usa marcadores como `[[NAV]]` en lugar de `{{...}}` y no des a las plantillas extensión .html, de lo contrario design-check y seo-check las tratarán como páginas a medio hacer. Construye el menú móvil con un `<details>` sin JS. Deja el nombre del negocio, la dirección y el teléfono como texto plano en el pie, y haz que los enlaces del pie y de las migas de pan midan al menos 44 píxeles de alto.
5. Convierte las imágenes al descargarlas con site_fetch: escribe la misma imagen como archivos separados con resize="480x", "800x" y "1200x", image_format="webp", quality=60 (site_fetch no recorta, así que pide el original con la proporción que necesites). Da a cada `<img>` srcset, sizes y width y height acordes con la proporción real. La imagen que es el elemento LCP de la página lleva fetchpriority="high" y no es lazy; todas las demás imágenes llevan loading="lazy" decoding="async". Si una imagen ocupa la primera pantalla móvil, sirve un recorte más pequeño al móvil con `<picture>`.
6. Añade `<link rel="icon" href="/favicon.svg" type="image/svg+xml">` a cada página y crea un apple-touch-icon de 180 píxeles a partir del logotipo con site_fetch y resize="180x180". Sin un enlace de favicon el navegador pide /favicon.ico, registra un 404 y Best Practices baja a 96.
7. Reglas del validador HTML: empieza cada documento con un `<!DOCTYPE html>` en mayúsculas; cada espacio en el texto visible de un enlace tel: debe ser ` `, incluido el que sigue a un prefijo como "Llama: 0212..."; no uses style=""; da a cada botón un type; pon aria-label solo en landmarks, formularios o elementos interactivos (en un ul o en un div sin rol era un error); nunca etiquetes dos landmarks con el mismo nombre; no dejes espacios en blanco al final de línea.
8. No saltes niveles de encabezado: en una página de listado, los títulos de tarjeta como h3 justo después del h1 bajaron Accessibility a 98. Cuando reutilices una tarjeta, fija su nivel de encabezado por página (h2 en una página de listado, h3 dentro de una sección h2).
9. Llama al atributo de movimiento data-reveal (design-check solo reconoce el movimiento por ese nombre). JS da el estado inicial oculto solo a los bloques que quedan fuera de pantalla cuando se abre la página y nunca oculta el contenido de la primera pantalla; usa transform, porque desvanecer con opacity reduce la auditoría de contraste. Pon el atributo en el bloque que envuelve un encabezado y su descripción, no solo en el encabezado; en la demo un encabezado en espera se deslizó sobre el párrafo de debajo. Con prefers-reduced-motion: reduce no se oculta nada y las animaciones se desactivan.
SEO, AEO y GEO:
1. Usa URL de estilo directorio con barra final. Rellena el bloque seo de site.config.js y ejecuta `node seo-stamp.mjs`. Escribe el schema específico de cada página en bloques `<script type="application/ld+json">` separados, enlazados al grafo base mediante @id. No anides BreadcrumbList dentro de @graph; mantenlo en su propio bloque, que es la única forma en que seo-check lo detecta.
2. Títulos de como máximo 60 caracteres (seo-check marca como error todo lo que tenga menos de 25 o más de 65), descripciones de 130 a 158 caracteres, un h1 por página y al menos 150 palabras de contenido principal en cada página. Las páginas de contacto, privacidad y listados tienden a quedarse cortas; añade información específica de la página, no relleno.
3. El primer párrafo de cada página responde directamente a la pregunta de la página. Escribe FAQ específicas de cada página con `<details>` y márcalas como FAQPage. Muestra el publisher y una fecha de "Última actualización" en un elemento `<time>`.
4. En robots.txt, escribe tu decisión con su motivo para GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Perplexity-User, Google-Extended, Applebot-Extended, Bingbot y CCBot, y apunta al sitemap. sitemap.xml lleva fechas lastmod reales. llms.txt contiene un resumen exacto del sitio en dos frases y una lista de las páginas importantes.
5. Nada de invenciones: no escribas reseñas, valoraciones, premios, cifras de clientes, certificados, horarios, coordenadas ni precios sin preguntarme. Deja fuera del schema cualquier campo que no conozcas.
Controles (en orden; no digas "listo" hasta que todos pasen):
1. Con site_run_command ejecuta `node chrome-stamp.mjs`, la compilación de Tailwind, `node seo-stamp.mjs`, `node seo-check.mjs` (0 errores, 0 advertencias), `node design-check.mjs` (0 errores; justifica cualquier advertencia que quede) y `npx html-validate "**/*.html"` (0 errores).
2. Revisa cada página con site_preview, view="mobile" y view="desktop". site_preview captura la página con el movimiento desactivado (reduced-motion); si falta contenido en esa captura, el estado estático está roto. En las capturas de página completa, los elementos fijos como una barra sticky pueden aparecer a mitad de página y las imágenes lazy más abajo pueden verse en blanco; no los cuentes como errores, pero comprueba sus rutas.
3. Ejecuta core_web_vitals para cada página con strategy="mobile" y strategy="desktop". Criterio de aceptación: en móvil, Performance de al menos 95 (objetivo de 99 a 100) y Accessibility, Best Practices y SEO en 100; en escritorio, las cuatro en 100. Para una página que no llegue, usa resources=true para encontrar el archivo que la retrasa, corrígelo y vuelve a medir. Después ejecuta seo_audit en la página de inicio con strategy="mobile" y runs=2. No debe quedar en la lista de solicitudes de red ninguna dirección ajena a tu dominio.
4. Sin desplazamiento horizontal con 360, 390, 768 y 1440 píxeles de ancho, áreas táctiles de al menos 44 píxeles y sin errores en la consola.
Comprobación de interacción: site_preview no puede mostrar hover, atajos de teclado ni diálogos. Dime cómo comprobar en un ordenador que la luz sigue al puntero sobre las tarjetas; que Ctrl K, Cmd K y / abren el menú; que escribir sin caracteres turcos sigue encontrando resultados ("erisilebilir" y similares); que las flechas y Enter me llevan a un artículo; que el foco vuelve al botón Buscar después de Esc; y que, con JavaScript desactivado, el enlace Buscar abre la página de proyectos.
Después de publicar: previsualiza con site_headers la regla `/assets/* Cache-Control: public, max-age=31536000, immutable` y aplícala una vez que yo la apruebe; a partir de entonces, versiona el nombre del archivo cada vez que cambien el CSS o el JS (site-v2.css y así sucesivamente). Envía sitemap.xml con gsc_submit_sitemap y comprueba la página de inicio con gsc_url_inspect. Al final, entrégame: una tabla de puntuaciones por página (página, las cuatro puntuaciones móviles, las cuatro de escritorio, LCP, CLS), el resultado de cada control y la lista de correcciones que aplicaste.