Completa antes de enviar
[nombre del servidor][nombre del estudio o fotógrafo][arquitectura, interiorismo o fotografía; qué tipo de trabajo][ciudad, distrito][año, número de personas, nombre del fundador][nombre, lugar, año, una frase][si lo hay][email][teléfono][número de WhatsApp][dirección][dominio]
En mi servidor de hosting de Opus Growth [nombre del servidor], crea un sitio de portfolio con scroll horizontal para [nombre del estudio o fotógrafo]. A qué me dedico: [arquitectura, interiorismo o fotografía; qué tipo de trabajo], ciudad: [ciudad, distrito], año de fundación y equipo: [año, número de personas, nombre del fundador]. Proyectos: [para cada uno, nombre, lugar, año, estado, superficie o tipo de sesión, resumen en una frase, planteamiento, enfoque, proceso, materiales o técnica, al menos dos enlaces a fotos]. Obras en curso: [nombre, lugar, año, una frase]. Registro profesional: [si lo hay]. Contacto: [email], [teléfono], [número de WhatsApp], [dirección]. Dominio: [dominio].
Páginas: inicio (/: hero, galería horizontal de proyectos, enfoque, FAQ), una página por proyecto (/projeler/[nombre-del-proyecto]/: imagen a ancho completo, ficha técnica, planteamiento, enfoque, proceso, materiales, una segunda imagen, enlace al siguiente proyecto), estudio y contacto (/ofis/, con la sección de contacto en el ancla #iletisim), privacidad y KVKK (/gizlilik/) y 404.html. Sin formulario; basta con email, teléfono y un enlace de WhatsApp con mensaje prellenado. Si un proyecto en curso no tiene página de detalle, su tarjeta no debe ser un enlace.
Estilo: un portfolio sereno, con aire de galería. Fondo color piedra caliza, texto oscuro, esquinas rectas y filetes finos; el color de acento aparece solo al pasar el cursor y en el foco. Una tipografía display serif de Fontshare (por ejemplo Erode) y una grotesca sencilla para el cuerpo (por ejemplo Archivo). En escritorio el hero ocupa toda la pantalla, de modo que la galería empieza debajo de la primera vista.
Reglas de movimiento (lo más difícil; síguelas al pie de la letra):
1. Librerías: GSAP 3.13 core, ScrollTrigger y Lenis, en la página de inicio y en las páginas de proyecto. SplitText y Swiper NO se cargan; en las páginas de estudio y privacidad no hay ningún script.
2. El HTML y CSS por defecto es una pila vertical: una columna en móvil, una cuadrícula de dos columnas en escritorio. Sin JS, con movimiento reducido y en móvil la galería se queda así.
3. Que sea el CSS, no el JS, quien construya el diseño horizontal: a partir de 768 px y cuando exista la clase `.anim`, el track es `display:flex`, `width:max-content` y `height:calc(100vh - altura del header)` desde el primer render, y el contenedor se desplaza por sí solo con `overflow-x:auto`. Así el diseño no cambia cuando llega el JS (CLS 0), y si la librería no llega nunca, la galería sigue pudiendo desplazarse de lado.
4. Con gsap.matchMedia, solo para `(min-width: 768px)`, fija el contenedor con ScrollTrigger, scrub 0.8, un end igual al desbordamiento del track ("+=" + (track.scrollWidth - innerWidth)) e invalidateOnRefresh activado; añade una clase que le dé al contenedor `overflow:hidden`. Una fina línea de progreso avanza con scaleX.
5. Revelado de imágenes: las imágenes de la galería se abren de izquierda a derecha con containerAnimation, de clip-path `inset(0 100% 0 0)` a `inset(0)`. Nunca ocultes el panel visible en la primera vista ni la imagen del hero. En móvil y en las páginas de proyecto, solo las imágenes que quedan por debajo de la pantalla al cargar se abren hacia arriba con clip-path.
6. Teclado: cuando Tab llega a una tarjeta que está fuera de pantalla en el track, el navegador desplaza el contenedor internamente y la tarjeta desaparece. En focusin, restablece el scrollLeft del contenedor a 0 y desplaza la página hasta la posición vertical en la que la tarjeta entra en vista (scrollTrigger.start + (end - start) * desplazamiento izquierdo de la tarjeta / desbordamiento total). Sin este paso, en la demo la tercera tarjeta a la que se llegaba con Tab quedaba fuera de pantalla.
7. Activa Lenis solo en dispositivos `(pointer: fine)` y conéctalo a ScrollTrigger.update; en los enlaces internos como #projeler, mueve el foco al destino después del desplazamiento.
Configuración y trampas conocidas (cada una costó puntos o rompió algo en la medición de la demo; no te saltes ninguna):
1. Si ya hay un sitio en producción en el servidor, avísame primero, porque la configuración inicial sobrescribe index.html y la carpeta assets. Previsualiza scaffold_site con confirm=false y llama con confirm=true tras mi aprobación. Después lee site_design_guide y site_seo_guide. Antes de escribir código, recoge la dirección de diseño (tipografía, color, rejilla, elemento distintivo, lenguaje de movimiento y las librerías que NO se cargarán) en un breve ART.md fuera de assets y muéstramelo.
2. Compila Tailwind, no lo cargues desde el CDN. Instala `npm i -D tailwindcss@3` con site_run_command y background=true, y vigílalo con site_command_status. Las rutas content de `tailwind.config.js` deben cubrir todos los archivos HTML, todas las plantillas y todos los archivos JS que escriben clases. `_src/tw.css` contiene una línea `@import "../assets/app.css";` y 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 las páginas y enlaza solo `/assets/site.css`. Recompila después de cada cambio de HTML.
3. Aloja las fuentes en local. Primero comprueba que la familia de Fontshare que elijas tenga las letras turcas (ş, ğ, ı, İ, ç, ö, ü): en la demo Tanker no tenía la ı sin punto y Outfit no tenía ş, ğ ni İ, así que esas letras caían en otra fuente en los titulares. Descarga el woff2 de cada peso por separado con site_fetch en `assets/fonts/`, escribe las reglas @font-face con `font-display: swap` en `assets/app.css` (design-check busca ahí la fuente personalizada) y comprueba el nombre de font-family en el CSS que descargaste. Precarga las fuentes de titulares y de cuerpo usadas above the fold en cada página con `<link rel="preload" as="font" type="font/woff2" crossorigin>`, antes de la hoja de estilos; no precargues más de dos archivos, porque precargar un tercer peso en la demo alargó el LCP móvil 0,2 segundos. La mayoría de las fuentes de Fontshare no tienen el signo ₺, así que escribe "TL" en los precios.
4. Haz que el menú y el footer sean HTML estático. No cargues chrome.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. Las plantillas no deben acabar en .html y deben usar marcadores como `[[NAV]]` en lugar de `{{...}}`. Construye el menú móvil sin JS usando `<details>`. Deja el nombre de la organización, la dirección y el teléfono como texto plano en el footer, y haz que cada elemento táctil mida al menos 44x44 px, incluidos los enlaces del footer y de las migas de pan.
5. Convierte las imágenes mientras las descargas con site_fetch: escribe la misma foto con resize="480x", "800x" y "1200x", image_format="webp", quality=65 en archivos separados. site_fetch nunca recorta; cuando una foto de Unsplash necesite otra proporción, descarga la versión recortada añadiendo parámetros como `?w=1600&h=1200&fit=crop` a su URL. Da a cada `<img>` srcset, sizes y un width y height que coincidan con la proporción real. La imagen principal above the fold lleva fetchpriority="high" y no es lazy; las demás llevan loading="lazy" decoding="async". Si la imagen principal ocupa toda la pantalla en móvil, sirve un recorte más pequeño con `<picture>`.
6. Escribe la versión SVG del logo como favicon.svg con site_write_file y añade `<link rel="icon" href="/favicon.svg" type="image/svg+xml">` a cada página. Sin favicon, el navegador registra un 404 y Best Practices baja a 96.
7. Reglas del validador HTML: empieza con un `<!DOCTYPE html>` en mayúsculas, usa ` ` para los espacios en el texto visible de los enlaces tel:, no uses nunca el atributo style="", da a cada botón type="button" y no pongas un `<details>` dentro de un `<figure>` después de su figcaption.
8. Descarga las librerías de movimiento (GSAP, ScrollTrigger, Lenis) de jsdelivr a `assets/vendor/` con site_fetch, fija la versión y cárgalas solo en la página de inicio y en las páginas de proyecto. No uses `<script defer>`; pon un pequeño cargador al final de la página que espere al evento load, dos llamadas a requestAnimationFrame y después a requestIdleCallback, y añada los scripts en orden con async=false. Un script de una línea en `<head>` añade la clase `js` a `<html>` y la clase `anim` salvo que prefers-reduced-motion sea "reduce"; sin `anim` el cargador no descarga nada. Define los estados iniciales ocultos solo bajo `.anim` y añade una animación CSS de seguridad que revele todos los elementos ocultos a los 2,5 segundos por si la librería nunca llega. En demos anteriores las librerías con defer empujaron el LCP móvil a 3,2 segundos; con este cargador las páginas midieron entre 99 y 100. Llama data-reveal al atributo de los elementos que se revelan al hacer scroll; design-check solo reconoce el movimiento por ese nombre.
SEO, AEO y GEO:
1. Usa URLs de estilo directorio con barra final (/projeler/[nombre-del-proyecto]/, /ofis/, /gizlilik/). Rellena el bloque seo de site.config.js y ejecuta `node seo-stamp.mjs`. Escribe el schema específico de cada página (inicio: WebPage y FAQPage; cada página de proyecto: WebPage con un CreativeWork como mainEntity, incluyendo dateCreated, locationCreated e image; estudio: AboutPage con una Person para el fundador; BreadcrumbList en las páginas interiores. Para un estudio de arquitectura o fotografía con oficina, businessType ProfessionalService) en bloques `<script type="application/ld+json">` separados y enlázalos con el grafo base mediante @id. No anides BreadcrumbList dentro de @graph; mantenlo en su propio bloque, porque es la única forma en que seo-check lo detecta.
2. Mantén los títulos en 60 caracteres o menos (seo-check marca como error todo lo que supere 65), las descripciones entre 120 y 158 caracteres, un solo h1 por página y ningún nivel de encabezado omitido (un h3 justo después del h1 bajó la accesibilidad a 98). Cada página necesita al menos 150 palabras de contenido principal; las páginas de contacto y privacidad suelen quedarse cortas.
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 la organización editora y una fecha de "Última actualización" en `<time>`.
4. En robots.txt, anota y justifica tu decisión 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. Da a sitemap.xml fechas lastmod reales. llms.txt contiene un resumen exacto del sitio en dos frases, los datos clave 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 de calidad (en orden; no digas "hecho" 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 avisos), `node design-check.mjs` (0 errores; justifica cualquier aviso que quede) y `npx html-validate "**/*.html"` (0 errores).
2. Revisa cada página con site_preview usando view="mobile" y view="desktop". site_preview captura la página con el movimiento desactivado (reduced-motion); si en esa captura falta contenido, el estado sin movimiento está roto. Las imágenes lazy más abajo pueden salir en blanco en la captura de página completa; no lo cuentes como error, pero comprueba sus rutas.
3. Ejecuta core_web_vitals con strategy="mobile" y strategy="desktop" para cada página. Criterio de aceptación: en móvil, Performance de al menos 95 y Accessibility, Best Practices y SEO en 100; en escritorio, las cuatro de al menos 95. Si una página no llega, encuentra el archivo lento con resources=true, 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 ninguna dirección ajena a tu propio dominio en la lista de peticiones de red.
4. Sin scroll horizontal a 360, 390, 768 y 1440 px, elementos táctiles de al menos 44 px y sin errores de consola. El sitio también debe verse impecable dentro de un marco de 390x790 y de 1440x898, como en las páginas de prompts.
Comprobación del movimiento: site_preview no puede mostrar movimiento, así que dime qué mirar en mi propio navegador. En escritorio, la galería debe quedarse fija arriba al llegar a la sección Proyectos, las tarjetas deben fluir hacia la izquierda al hacer scroll hacia abajo, las imágenes deben abrirse de izquierda a derecha y la línea de progreso debe llenarse; tras la última tarjeta, la página vuelve al flujo normal. Al moverse con Tab entre tarjetas, la tarjeta enfocada debe estar siempre en pantalla. En móvil y con reducir movimiento activado, la galería es una pila de tarjetas y nada se queda fijo.
Después de publicar: previsualiza con site_headers la regla `/assets/* Cache-Control: public, max-age=31536000, immutable` y aplícala tras mi aprobación; a partir de ahí, versiona el nombre del archivo cada vez que cambie el CSS o el JS (site-v2.css, etc.). Envía sitemap.xml con gsc_submit_sitemap y comprueba la página de inicio con gsc_url_inspect. Por último, entrégame: una tabla de puntuaciones por página (página, las cuatro puntuaciones móviles, las cuatro de escritorio, LCP, CLS), los resultados de los controles, el resultado de las interacciones que probaste en un navegador y la lista de correcciones que aplicaste.