From 95e4cca98e0dbc6c8be37c593f70a83612a59e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20G=C3=B3mez=20Jim=C3=A9nez?= Date: Fri, 7 Aug 2026 19:01:42 +0200 Subject: [PATCH 1/4] feat(gauntlet): la landing del Gauntlet Challenge de agosto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Una página que anuncia el challenge y no guarda nada: sin acceso a cloud-api, cualquier cosa que pareciera un formulario sería mentir. El alta va al Google Form, y la entrega y la votación se cuentan, no se construyen aquí. Qué entra: - /gauntlet y /es/gauntlet sobre el patrón `_`-prefijado, con el calendario de agosto marcado día a día y cada casilla diciendo qué pasa ese día. Cabe entero en una pantalla. - La sección del prompt original, anotada al margen como la captura de la que salió, con un botón que lo copia al portapapeles. Es el único JavaScript propio de la página y no manda nada a ninguna parte. - El prompt vive en src/data/gauntletPrompt.ts, fuera de i18n: no se traduce, y tenerlo en un sitio evita que lo que se lee y lo que se copia acaben diciendo cosas distintas. Un test lo compara con el original palabra por palabra. - Las fechas (workshop, construcción, cierre el 28, votación) salen de eventos.json y las lee src/lib/gauntlet.ts con el reloj como parámetro, así que el estado de la página es testeable. - La fila del Gauntlet en /events enlaza a la landing; el resto de la agenda sigue siendo texto. Sitemap y Event de schema.org al día. Co-Authored-By: Claude Opus 5 --- i18n/en.json | 223 +++++++ i18n/es.json | 223 +++++++ src/components/nan/StructuredData.astro | 21 +- src/data/eventos.json | 8 +- src/data/gauntletPrompt.ts | 46 ++ src/lib/agenda.ts | 6 + src/lib/gauntlet.ts | 140 ++++ src/pages/_events.astro | 16 +- src/pages/_gauntlet.astro | 792 +++++++++++++++++++++++ src/pages/es/gauntlet.astro | 8 + src/pages/gauntlet.astro | 8 + src/pages/sitemap.xml.ts | 11 +- src/tests/landing/eventsData.test.ts | 17 + src/tests/landing/gauntletPage.test.ts | 104 +++ src/tests/landing/gauntletPrompt.test.ts | 81 +++ src/tests/landing/i18nParity.test.ts | 68 ++ src/tests/lib/gauntlet.test.ts | 144 +++++ src/tests/lib/sitemap.test.ts | 9 +- 18 files changed, 1919 insertions(+), 6 deletions(-) create mode 100644 src/data/gauntletPrompt.ts create mode 100644 src/lib/gauntlet.ts create mode 100644 src/pages/_gauntlet.astro create mode 100644 src/pages/es/gauntlet.astro create mode 100644 src/pages/gauntlet.astro create mode 100644 src/tests/landing/gauntletPage.test.ts create mode 100644 src/tests/landing/gauntletPrompt.test.ts create mode 100644 src/tests/landing/i18nParity.test.ts create mode 100644 src/tests/lib/gauntlet.test.ts diff --git a/i18n/en.json b/i18n/en.json index 8874353..125be6f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -520,6 +520,229 @@ "statusBody": "The registration window has closed. We hope to see you at the next NaN hackathon!" } }, + "gauntlet": { + "metaTitle": "Gauntlet Challenge — NaN", + "metaDesc": "August at NaN: one prompt fires up a system that builds, critiques and corrects itself. Build until August 28, then the community votes.", + "heroEyebrow": "// gauntlet challenge", + "heroTitle1": "Gauntlet", + "heroTitle2": "Challenge.", + "heroSub": "One prompt. A system that builds, critiques and corrects itself. Build until August 28 — then the community votes.", + "status": { + "pre": "Starts in {n} days", + "preOne": "Starts tomorrow", + "preZero": "Starts today", + "live": "{n} days left to submit", + "liveOne": "Tomorrow is the last day to submit", + "liveZero": "Last day to submit", + "voting": "Voting is open", + "closed": "This edition is closed" + }, + "ctaSecondary": "Join the community", + "ctaVote": "Voting is on Discord", + "ctaClosed": "See community projects", + "form": { + "url": "https://docs.google.com/forms/d/e/1FAIpQLSdsu4TfUcWHv_DsDYge1hGgZN4naQy-ju_9ettH0j98X4Dx-Q/viewform", + "cta": "Sign up for the challenge", + "note": "Opens a Google Form in a new tab · 4 fields, half a minute", + "newTab": "opens in a new tab", + "soonTitle": "signup opens at the kickoff", + "soon": "The form goes live with the workshop on August 7. We announce the link on NaN's Discord." + }, + "idea": { + "label": "the key idea", + "title": "A prompt doesn't do the work.", + "sub": "It fires up an autonomous system that builds, observes, critiques and corrects.", + "steps": [ + { + "n": "01", + "name": "Goal", + "body": "One human instruction stating what has to exist." + }, + { + "n": "02", + "name": "Builders", + "body": "Agents that write it. Each one owns a piece and ships it." + }, + { + "n": "03", + "name": "Critics", + "body": "Agents that review the work against proof, not impressions." + }, + { + "n": "04", + "name": "Evidence", + "body": "Screenshots, tests, metrics. Artifacts you can inspect." + }, + { + "n": "05", + "name": "Iteration", + "body": "Beats the reference: it lands. Doesn't: back to the builder." + } + ], + "quote": "The edge isn't thinking harder. It's closing the loop with feedback you can observe.", + "note": "\"One prompt\" means one initial human instruction — not one call, and not one agent." + }, + "prompt": { + "label": "the prompt", + "title": "This is the whole prompt. Copy it and make it yours.", + "lead": "The one from the experiment that started all this. There is no trick underneath: one instruction, sub-agents that own a piece each, and a critic that refuses to sign off until the thing survives the comparison.", + "frame": "Gauntlet Prompt", + "copy": "Copy the prompt", + "copied": "Copied", + "copyFail": "Select it and copy it by hand", + "foot": "Copied verbatim, in English: it is the original. Yours does not have to be a shooter — the theme is open.", + "notes": { + "ref": "Reference", + "fanout": "Fan out sub-agents, each owning one element until it is perfect", + "loop": "Every sub-agent inside a loop", + "critic": "One critic sub-agent per builder sub-agent", + "harsh": "Be a harsh critic and do not stop until it is indistinguishable from the original", + "ultra": "Super saiyan 4, ultra instinct, sharingan, find the one piece" + } + }, + "design": { + "label": "how it's designed", + "title": "More agents doesn't mean better results.", + "sub": "The lesson from the experiment: what breaks a loop isn't the number of agents, it's who owns what.", + "parallelTitle": "Parallelise what's independent", + "parallelItems": [ + "Lighting", + "Materials", + "Colour" + ], + "parallelBody": "Pieces that don't touch each other can run at the same time without stepping on anyone.", + "ownerTitle": "One owner for what's coupled", + "ownerItems": [ + "Weapons", + "Sound", + "Enemies" + ], + "ownerBody": "Systems that depend on each other need a single hand, or they drift apart.", + "quote": "The Gauntlet isn't about spawning a hundred agents. It's about designing ownership well." + }, + "calendar": { + "label": "calendar", + "title": "August is a season, not a day.", + "month": "August 2026", + "weekdays": [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun" + ], + "tbd": "Presentation day · to be confirmed", + "short": { + "workshop": "Kickoff", + "build": "Build", + "close": "Closes", + "voting": "Vote" + } + }, + "join": { + "label": "how to take part", + "title": "Build and publish. No permission needed.", + "sub": "You sign up, you build with what you already have on NaN and you publish. Nobody hands out slots.", + "steps": [ + "Find the challenge", + "Build", + "Publish", + "Update", + "Mark the final run" + ], + "reqTitle": "what you need", + "reqs": [ + "Be a NaN member", + "Use open models available on NaN", + "Document the loop and the human intervention", + "Submit before August 28" + ], + "signupTitle": "signup is always open", + "signupBody": "There's no early cut-off: the form stays open until closing day. Nobody is left out for showing up late — the calendar is there to spread the work, not to shut a door." + }, + "submit": { + "label": "the submission", + "title": "What you submit has to prove what you built, and how.", + "sub": "One submission per project, editable until it closes.", + "requiredLabel": "required", + "required": [ + "Name + description", + "Model used", + "16:9 cover image", + "Loop architecture", + "Repository", + "Human intervention", + "Initial prompt", + "Result + learnings" + ], + "requiredNote": "The last two are the ones that prove what you built and how.", + "recommendedLabel": "recommended", + "recommended": [ + { + "name": "Video", + "why": "Shows the result" + }, + { + "name": "Demo", + "why": "Lets people try it" + }, + { + "name": "Baseline vs final", + "why": "Makes the improvement visible" + }, + { + "name": "Tokens and time", + "why": "Explain the cost" + }, + { + "name": "Logs and traces", + "why": "Make it auditable" + }, + { + "name": "Diagram", + "why": "Clarifies the architecture" + } + ], + "rule": "the image is required · the video is recommended · a public demo is optional", + "notes": [ + "Repository: it can stay private during August, but it has to be opened at closing time — or shared with the organisers so it can be verified.", + "Video: we don't host it. A YouTube, Loom or similar URL works." + ], + "whereTitle": "where to submit", + "whereBody": "The submission form will appear here later." + }, + "after": { + "label": "after the close", + "title": "The community picks who presents.", + "sub": "No jury. What you ship gets shown, the community votes, and the most-voted projects present live.", + "steps": [ + { + "when": "Aug 28", + "name": "Entry closes", + "body": "Signup and submissions stop." + }, + { + "when": "A few days", + "name": "Community voting", + "body": "Everything submitted gets shown, and the community votes." + }, + { + "when": "To be confirmed", + "name": "Presentations", + "body": "The most-voted projects present live." + } + ], + "tbdLabel": "date to be confirmed", + "tbdBody": "We're looking for a day that works for everyone, so it gets announced on Discord rather than pinned down now.", + "note": "Voting doesn't live on this page: it happens on NaN's Discord." + }, + "close": { + "title": "One prompt. A whole system.", + "text": "August at NaN is a season: build it, let it be critiqued, correct it and ship something you can prove. Signup is open until the 28th." + } + }, "community": { "metaTitle": "Community — NaN", "metaDesc": "NaN is the springboard for builders shipping with open AI. Build in public, together.", diff --git a/i18n/es.json b/i18n/es.json index ebace28..4afec49 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -520,6 +520,229 @@ "statusBody": "La ventana de inscripción se ha cerrado. ¡Esperamos verte en el próximo hackathon de NaN!" } }, + "gauntlet": { + "metaTitle": "Gauntlet Challenge — NaN", + "metaDesc": "Agosto en NaN: un prompt activa un sistema que construye, critica y se corrige solo. Construye hasta el 28 de agosto y después vota la comunidad.", + "heroEyebrow": "// gauntlet challenge", + "heroTitle1": "Gauntlet", + "heroTitle2": "Challenge.", + "heroSub": "Un prompt. Un sistema que construye, critica y se corrige solo. Construye hasta el 28 de agosto. Después vota la comunidad.", + "status": { + "pre": "Empieza en {n} días", + "preOne": "Empieza mañana", + "preZero": "Empieza hoy", + "live": "Quedan {n} días para el cierre", + "liveOne": "Mañana es el último día para entregar", + "liveZero": "Último día para entregar", + "voting": "Votaciones abiertas", + "closed": "Edición cerrada" + }, + "ctaSecondary": "Únete a la comunidad", + "ctaVote": "La votación es en Discord", + "ctaClosed": "Ver los proyectos de la comunidad", + "form": { + "url": "https://docs.google.com/forms/d/e/1FAIpQLSdsu4TfUcWHv_DsDYge1hGgZN4naQy-ju_9ettH0j98X4Dx-Q/viewform", + "cta": "Apúntate al challenge", + "note": "Abre un Google Form en otra pestaña · 4 campos, medio minuto", + "newTab": "se abre en otra pestaña", + "soonTitle": "la inscripción abre en el kickoff", + "soon": "El formulario se publica con el workshop del 7 de agosto. El enlace se anuncia en el Discord de NaN." + }, + "idea": { + "label": "la idea clave", + "title": "Un prompt no hace el trabajo.", + "sub": "Activa un sistema autónomo que construye, observa, critica y corrige.", + "steps": [ + { + "n": "01", + "name": "Objetivo", + "body": "Una instrucción humana que dice qué tiene que existir." + }, + { + "n": "02", + "name": "Builders", + "body": "Agentes que lo construyen. Cada uno es dueño de una pieza y la entrega." + }, + { + "n": "03", + "name": "Críticos", + "body": "Agentes que revisan el trabajo con pruebas, no con impresiones." + }, + { + "n": "04", + "name": "Evidencia", + "body": "Capturas, tests, métricas. Artefactos que se pueden inspeccionar." + }, + { + "n": "05", + "name": "Iteración", + "body": "Supera la referencia: entra. No la supera: vuelve al builder." + } + ], + "quote": "La ventaja no está en pensar más, sino en cerrar el bucle con feedback observable.", + "note": "«Un prompt» es una instrucción humana inicial, no una sola llamada ni un único agente." + }, + "prompt": { + "label": "el prompt", + "title": "Este es el prompt entero. Cópialo y adáptalo.", + "lead": "El del experimento que dio pie a todo esto. Debajo no hay más truco: una instrucción, subagentes que se llevan una pieza cada uno y un crítico que no da el visto bueno hasta que la cosa aguanta la comparación.", + "frame": "Gauntlet Prompt", + "copy": "Copiar el prompt", + "copied": "Copiado", + "copyFail": "Selecciónalo y cópialo a mano", + "foot": "Se copia tal cual, en inglés: es el original. El tuyo no tiene por qué ser un shooter — el tema es libre.", + "notes": { + "ref": "Referencia", + "fanout": "Despliega subagentes que se encarguen individualmente de cada elemento para que quede perfecto", + "loop": "Cada subagente dentro de un loop", + "critic": "Un subagente crítico por cada agente constructor", + "harsh": "Sé muy crítico y no pares hasta que no se distinga la versión original", + "ultra": "Super saiyan 4, ultrainstinto, sharingan, encuentra el one piece" + } + }, + "design": { + "label": "cómo se diseña", + "title": "Más agentes no significa mejores resultados.", + "sub": "La lección del experimento: lo que rompe un bucle no es el número de agentes, sino quién es dueño de qué.", + "parallelTitle": "Paraleliza lo independiente", + "parallelItems": [ + "Luz", + "Materiales", + "Color" + ], + "parallelBody": "Las piezas que no se tocan pueden ir a la vez sin pisarse.", + "ownerTitle": "Un solo responsable para lo acoplado", + "ownerItems": [ + "Armas", + "Sonido", + "Enemigos" + ], + "ownerBody": "Los sistemas que dependen entre sí necesitan una sola mano, o se separan.", + "quote": "El Gauntlet no es crear cien agentes: es diseñar bien la propiedad del trabajo." + }, + "calendar": { + "label": "calendario", + "title": "Agosto es una temporada, no un día.", + "month": "agosto 2026", + "weekdays": [ + "Lun", + "Mar", + "Mié", + "Jue", + "Vie", + "Sáb", + "Dom" + ], + "tbd": "Día de presentaciones · por confirmar", + "short": { + "workshop": "Kickoff", + "build": "Construir", + "close": "Cierre", + "voting": "Votar" + } + }, + "join": { + "label": "cómo participar", + "title": "Construye y publica. Sin pedir permiso.", + "sub": "Te apuntas, construyes con lo que ya tienes en NaN y publicas. Aquí nadie reparte plazas.", + "steps": [ + "Descubre el reto", + "Construye", + "Publica", + "Actualiza", + "Marca la ejecución final" + ], + "reqTitle": "lo que hace falta", + "reqs": [ + "Ser miembro de NaN", + "Usar modelos abiertos disponibles en NaN", + "Documentar el loop y la intervención humana", + "Entregar antes del 28 de agosto" + ], + "signupTitle": "la inscripción está siempre abierta", + "signupBody": "No hay ventana que se cierre antes: el formulario sigue abierto hasta el día del cierre. Nadie se queda fuera por llegar tarde — el calendario está para repartir el trabajo, no para levantar una puerta." + }, + "submit": { + "label": "la entrega", + "title": "Lo que entregas tiene que probar qué construiste y cómo.", + "sub": "Una entrega por proyecto, editable hasta el cierre.", + "requiredLabel": "obligatorio", + "required": [ + "Nombre + descripción", + "Modelo utilizado", + "Portada 16:9", + "Arquitectura del loop", + "Repositorio", + "Intervención humana", + "Prompt inicial", + "Resultado + aprendizajes" + ], + "requiredNote": "Los dos últimos son los que prueban qué se construyó y cómo.", + "recommendedLabel": "recomendado", + "recommended": [ + { + "name": "Vídeo", + "why": "Demuestra el resultado" + }, + { + "name": "Demo", + "why": "Permite probarlo" + }, + { + "name": "Baseline vs. final", + "why": "Hace visible la mejora" + }, + { + "name": "Tokens y tiempo", + "why": "Explican el coste" + }, + { + "name": "Logs y trazas", + "why": "Permiten auditar" + }, + { + "name": "Diagrama", + "why": "Aclara la arquitectura" + } + ], + "rule": "la imagen es obligatoria · el vídeo es recomendado · la demo pública es opcional", + "notes": [ + "Repositorio: puede seguir privado durante agosto, pero tiene que abrirse al cierre — o compartirse con la organización para poder verificarlo.", + "Vídeo: no lo alojamos. Vale una URL de YouTube, Loom o similares." + ], + "whereTitle": "por dónde se entrega", + "whereBody": "El formulario de entrega aparecerá aquí más adelante." + }, + "after": { + "label": "después del cierre", + "title": "La comunidad elige quién presenta.", + "sub": "Sin jurado. Lo entregado se enseña, la comunidad vota y los proyectos más votados se presentan en directo.", + "steps": [ + { + "when": "28 ago", + "name": "Se cierra la entrada", + "body": "Se paran las inscripciones y las entregas." + }, + { + "when": "Unos días", + "name": "Votaciones de la comunidad", + "body": "Se enseña todo lo entregado y la comunidad vota." + }, + { + "when": "Por concretar", + "name": "Presentaciones", + "body": "Presentan los proyectos más votados." + } + ], + "tbdLabel": "fecha por confirmar", + "tbdBody": "Buscamos un día que le venga bien a todo el mundo, así que se anuncia en Discord en vez de fijarlo ahora.", + "note": "La votación no vive en esta página: pasa en el Discord de NaN." + }, + "close": { + "title": "Un prompt. Un sistema entero.", + "text": "Agosto en NaN es una temporada: construye, deja que te critiquen, corrige y entrega algo que se pueda probar. La inscripción está abierta hasta el 28." + } + }, "community": { "metaTitle": "Comunidad — NaN", "metaDesc": "NaN es el trampolín para builders que construyen con IA abierta. Build in public, en comunidad.", diff --git a/src/components/nan/StructuredData.astro b/src/components/nan/StructuredData.astro index 9640ccc..9bfaf69 100644 --- a/src/components/nan/StructuredData.astro +++ b/src/components/nan/StructuredData.astro @@ -12,7 +12,7 @@ import eventos from '../../data/eventos.json'; interface Props { lang: Locale; - page: 'home' | 'events' | 'other'; + page: 'home' | 'events' | 'gauntlet' | 'other'; canonical: string; origin: string; } @@ -65,6 +65,25 @@ if (page === 'events') { }); } +if (page === 'gauntlet') { + // Solo lo que la propia página afirma: nombre, fechas de construcción y que + // es un evento en línea de NaN. Ni el día de presentaciones (no tiene fecha) + // ni premios (sin definir) entran aquí. + graph.push({ + '@type': 'Event', + '@id': `${canonical}#event`, + name: 'Gauntlet Challenge', + description: t.gauntlet.metaDesc, + startDate: eventos.gauntlet.start, + endDate: eventos.gauntlet.close, + eventStatus: 'https://schema.org/EventScheduled', + eventAttendanceMode: 'https://schema.org/OnlineEventAttendanceMode', + location: { '@type': 'VirtualLocation', url: canonical }, + url: canonical, + organizer: { '@id': `${origin}/#organization` }, + }); +} + const jsonld = { '@context': 'https://schema.org', '@graph': graph }; --- diff --git a/src/data/eventos.json b/src/data/eventos.json index 65bd3e2..8cecafa 100644 --- a/src/data/eventos.json +++ b/src/data/eventos.json @@ -2,8 +2,14 @@ "agenda": [ { "date": "2026-07-31", "type": "workshop", "title": { "en": "Agent evaluation and tracing", "es": "Evaluación y trazabilidad de agentes" }, "by": "@Jasonjimnz" }, { "date": "2026-08-07", "type": "workshop", "title": { "en": "#5 NaN Workshop: Gauntlet Loop", "es": "#5 Workshop de NaN: Gauntlet Loop" }, "by": "@sahul_125" }, - { "date": "2026-08-08", "until": "2026-08-31", "type": "event", "title": "Gauntlet Challenge" } + { "date": "2026-08-08", "until": "2026-08-28", "type": "event", "title": "Gauntlet Challenge", "href": "/gauntlet" } ], + "gauntlet": { + "workshop": "2026-08-07", + "start": "2026-08-08", + "close": "2026-08-28", + "votingEnd": "2026-08-31" + }, "workshops": [ { "title": { "en": "Workshop: AI Agent Observability", "es": "Workshop: Observabilidad en Agentes de IA" }, "desc": { "en": "How to instrument AI agents so you can trace every decision, measure what is going on in production and debug when something breaks.", "es": "Cómo instrumentar agentes de IA para poder trazar cada decisión, medir qué está pasando en producción y depurar cuando algo se rompe." }, "date": "31 jul 2026", "duration": "1:33:28", "thumb": "/img/events/observabilidad-agentes.webp", "tags": ["agents", "observability", "IA"] }, { "title": "Workshop: Opencode", "date": "24 jul 2026", "duration": "1:03:11", "thumb": "/img/events/opencode.webp", "tags": ["opencode", "coding agent", "dev tools"] }, diff --git a/src/data/gauntletPrompt.ts b/src/data/gauntletPrompt.ts new file mode 100644 index 0000000..ec241b0 --- /dev/null +++ b/src/data/gauntletPrompt.ts @@ -0,0 +1,46 @@ +/** + * El prompt original del Gauntlet, el del experimento que dio pie al challenge. + * + * Vive aquí y NO en los diccionarios de i18n por dos razones: no se traduce + * —lo que se copia es el original, en inglés, tal cual se escribió— y tenerlo + * en un solo sitio evita que la versión que se lee y la que se copia acaben + * diciendo cosas distintas. La página pinta los trozos marcados y el botón + * copia `promptText()`: las dos salidas nacen de esta misma lista. + */ + +/** Los subrayados de color. Cada uno tiene su nota al margen, menos `stack`. */ +export type PromptInk = 'ref' | 'fanout' | 'loop' | 'critic' | 'harsh' | 'stack' | 'ultra'; + +export type PromptPiece = { t: string; ink?: PromptInk }; + +export const GAUNTLET_PROMPT: PromptPiece[][] = [ + [ + { t: 'I want you to build a first-person shooter at the level of the most recent ' }, + { t: 'Call of Duty games', ink: 'ref' }, + { + t: '. It should be utterly perfect, visually beautiful, with every single thing done at AAA quality—from textures to physics to anything you could think of.', + }, + ], + [ + { t: 'Fan out sub-agents and have sub-agents tackle each one individually', ink: 'fanout' }, + { t: ' so that the game is utterly perfect. You should ' }, + { t: '/loop', ink: 'loop' }, + { t: ' on each item and have a ' }, + { t: 'separate sub-agent check it visually to ensure it', ink: 'critic' }, + { t: ' looks triple A. That separate sub-agent should be a really ' }, + { t: 'harsh critic', ink: 'harsh' }, + { t: ", and if it doesn't look triple A, it should keep going." }, + ], + [ + { + t: "Don't stop until each sub-agent is utterly wowed with the quality when compared with the actual Call of Duty game. It should literally compare them side by side blind and say which one looks better. Do this in ", + }, + { t: 'ThreeJS', ink: 'stack' }, + { t: '. ' }, + { t: "/loop until it's utterly perfect. Fan out sub-agents and ultracode.", ink: 'ultra' }, + ], +]; + +/** El prompt en plano, que es lo que acaba en el portapapeles. */ +export const promptText = (paragraphs: PromptPiece[][] = GAUNTLET_PROMPT) => + paragraphs.map((p) => p.map((piece) => piece.t).join('')).join('\n\n'); diff --git a/src/lib/agenda.ts b/src/lib/agenda.ts index 8e37709..7b5e340 100644 --- a/src/lib/agenda.ts +++ b/src/lib/agenda.ts @@ -21,6 +21,12 @@ export type AgendaItem = { type: string; title: Localized; by?: string; + /** + * Ruta interna con la landing del evento, sin prefijo de idioma: la fila la + * pinta con `withLang`. Opcional a propósito — la mayoría de entradas no + * tienen página propia y siguen siendo texto. + */ + href?: string; }; export const loc = (v: Localized, lang: Locale): string => (typeof v === 'string' ? v : v[lang]); diff --git a/src/lib/gauntlet.ts b/src/lib/gauntlet.ts new file mode 100644 index 0000000..52de040 --- /dev/null +++ b/src/lib/gauntlet.ts @@ -0,0 +1,140 @@ +/** + * En qué momento del Gauntlet Challenge estamos. + * + * Vive aquí y no en la página por lo mismo que `agenda.ts`: es aritmética de + * fechas, se equivoca en silencio (un día de más y el CTA de inscripción + * desaparece mientras el formulario sigue abierto) y con el reloj como + * parámetro se prueba sin depender de cuándo se ejecute el test. + * + * Las fechas NO se cablean aquí: vienen de `data/eventos.json`, porque el fin + * de la votación todavía no está cerrado ("unos días" desde el 28) y tiene que + * poder cambiarse sin tocar código. + * + * Importante: la fase solo cambia texto y enlaces. No habilita ni deshabilita + * nada, porque no hay nada que habilitar — la inscripción es un Google Form + * externo. Es deliberado: el reloj del Worker es UTC y cerca de medianoche en + * Canarias o LATAM va un día adelantado, así que un cálculo de fase nunca puede + * ser lo que deje a alguien fuera. + */ + +import { parseISO, fmtISO } from './agenda'; + +export type GauntletDates = { + /** Workshop de kickoff. */ + workshop: string; + /** Primer día para construir. */ + start: string; + /** Último día para inscribirse y entregar, inclusivo. */ + close: string; + /** Último día de votación, inclusivo. Provisional hasta que se cierre. */ + votingEnd: string; +}; + +export type GauntletPhase = 'pre' | 'live' | 'voting' | 'closed'; + +export type GauntletState = { + phase: GauntletPhase; + /** + * Días que faltan para el hito de la fase: para el arranque en `pre`, para + * el cierre en `live`. En `voting` y `closed` no hay cuenta atrás (la + * votación no tiene final anunciado), así que es 0. + */ + days: number; +}; + +const DAY = 86_400_000; + +/** Días completos entre dos ISO, positivo si `to` es posterior. */ +export const daysBetween = (from: string, to: string): number => + Math.round((parseISO(to).getTime() - parseISO(from).getTime()) / DAY); + +export function gauntletState(dates: GauntletDates, today: Date): GauntletState { + const iso = fmtISO(today); + + if (iso < dates.start) return { phase: 'pre', days: daysBetween(iso, dates.start) }; + // El día del cierre sigue siendo `live`: se entrega hasta el final del día. + if (iso <= dates.close) return { phase: 'live', days: daysBetween(iso, dates.close) }; + if (iso <= dates.votingEnd) return { phase: 'voting', days: 0 }; + return { phase: 'closed', days: 0 }; +} + +/** Qué pasa cada día del mes en la rejilla del calendario. */ +export type DayKind = 'idle' | 'workshop' | 'build' | 'close' | 'voting'; + +export type CalendarCell = { day: number; iso: string; kind: DayKind }; + +export type CalendarMonth = { + /** Huecos vacíos antes del día 1, con la semana empezando en lunes. */ + lead: number; + cells: CalendarCell[]; +}; + +/** ISO de un día concreto sin pasar por Date, que ya trae bastantes zonas. */ +const isoOf = (year: number, month: number, day: number) => + `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + +/** + * La rejilla del mes en el que se construye, marcada con lo que pasa cada día. + * + * El mes sale de `dates.start`, no de un literal: si la edición se mueve a + * septiembre, el calendario se mueve con ella. Los días fuera de las ventanas + * (antes del workshop, después de la votación) quedan en `idle` — se pintan, + * pero apagados, para que el mes se lea entero y no solo el trozo activo. + */ +export function calendarMonth(dates: GauntletDates): CalendarMonth { + const [year, month] = dates.start.split('-').map(Number); + const days = new Date(year, month, 0).getDate(); + // getDay() cuenta desde el domingo; aquí la semana empieza en lunes. + const lead = (new Date(year, month - 1, 1).getDay() + 6) % 7; + + const cells = Array.from({ length: days }, (_, i) => { + const day = i + 1; + const iso = isoOf(year, month, day); + const kind: DayKind = + iso === dates.workshop + ? 'workshop' + : iso === dates.close + ? 'close' + : iso >= dates.start && iso < dates.close + ? 'build' + : iso > dates.close && iso <= dates.votingEnd + ? 'voting' + : 'idle'; + return { day, iso, kind }; + }); + + return { lead, cells }; +} + +/** + * El texto de estado del hero. Hoy y mañana tienen frase propia porque + * "empieza en 0 días" no lo dice nadie. + */ +export function statusText( + state: GauntletState, + strings: { + pre: string; + preOne: string; + preZero: string; + live: string; + liveOne: string; + liveZero: string; + voting: string; + closed: string; + }, +): string { + switch (state.phase) { + case 'pre': + if (state.days === 0) return strings.preZero; + if (state.days === 1) return strings.preOne; + return strings.pre.replace('{n}', String(state.days)); + case 'live': + if (state.days === 0) return strings.liveZero; + if (state.days === 1) return strings.liveOne; + return strings.live.replace('{n}', String(state.days)); + case 'voting': + return strings.voting; + default: + return strings.closed; + } +} diff --git a/src/pages/_events.astro b/src/pages/_events.astro index 54487ff..7c152f3 100644 --- a/src/pages/_events.astro +++ b/src/pages/_events.astro @@ -1,7 +1,7 @@ --- import NanPage from '../layouts/NanPage.astro'; import eventos from '../data/eventos.json'; -import { getLang, useT } from '../lib/i18n'; +import { getLang, useT, withLang } from '../lib/i18n'; import { calendarDays, loc as resolve, @@ -79,7 +79,13 @@ const hack = eventos.hackathons[0];
  • {spanLabel(a, lang)} {tt.types[a.type as keyof typeof tt.types]} - {loc(a.title)} + {/* Solo las entradas con landing propia son clicables; el resto + sigue siendo texto, para no prometer una página que no existe. */} + + {a.href + ? {loc(a.title)} + : loc(a.title)} + {a.by}
  • ))} @@ -263,6 +269,12 @@ const hack = eventos.hackathons[0]; background: var(--color-violet); padding: 3px 8px; justify-self: start; align-self: center; } .agenda__title { font-family: var(--font-serif); font-size: clamp(17px, 1.5vw, 21px); color: var(--color-text); line-height: 1.2; } + .agenda__link { + color: inherit; text-decoration: none; + border-bottom: 1px solid color-mix(in srgb, var(--color-violet) 55%, transparent); + transition: color var(--dur-micro) var(--ease), border-color var(--dur-micro) var(--ease); + } + .agenda__link:hover { color: var(--color-link); border-bottom-color: var(--color-link); } .agenda__by { font-size: 13px; color: var(--color-muted); } .agenda__empty { margin-top: var(--space-6); color: var(--color-muted); font-family: var(--font-serif); font-size: 17px; } diff --git a/src/pages/_gauntlet.astro b/src/pages/_gauntlet.astro new file mode 100644 index 0000000..300075b --- /dev/null +++ b/src/pages/_gauntlet.astro @@ -0,0 +1,792 @@ +--- +/** + * Landing del Gauntlet Challenge (agosto 2026). + * + * Es una PÁGINA DE ANUNCIO: no guarda un solo dato. La inscripción vive en un + * Google Form externo, y la entrega y la votación se explican pero no se + * construyen — eso necesita el backend cloud-api, al que esta web no tiene + * acceso. De ahí que la página no lleve ni una isla ni un endpoint. El único + * JavaScript propio es el botón que copia el prompt al portapapeles: no manda + * nada a ninguna parte y no necesita hidratar nada. + * + * Regla que sostiene ese alcance: cada pieza que se menciona pero no existe + * dice dónde va a ocurrir de verdad. En la práctica, el Discord de NaN — que + * no tiene invitación pública, así que el destino es /community, la página que + * explica cómo entrar. Nada de botones muertos. + * + * La página NO está prerenderizada, así que la fase se recalcula en cada + * petición con el reloj del Worker (UTC): cerca de medianoche en Canarias o + * LATAM puede ir un día adelantado. Es inofensivo a propósito — la fase solo + * cambia texto y enlaces, nunca deja a nadie fuera (ver lib/gauntlet.ts). + */ +import NanPage from '../layouts/NanPage.astro'; +import StructuredData from '../components/nan/StructuredData.astro'; +import eventos from '../data/eventos.json'; +import { getLang, useT, withLang } from '../lib/i18n'; +import { spanLabel } from '../lib/agenda'; +import { calendarMonth, gauntletState, statusText, type GauntletDates } from '../lib/gauntlet'; +import { GAUNTLET_PROMPT as P, promptText } from '../data/gauntletPrompt'; + +const lang = getLang(Astro.url); +const tt = useT(lang).gauntlet; + +const site = Astro.site ?? new URL('https://nan.builders'); +const canonical = new URL(Astro.url.pathname.replace(/\/+$/, '') || '/', site).href; + +const dates = eventos.gauntlet as GauntletDates; +const state = gauntletState(dates, new Date()); +const status = statusText(state, tt.status); + +// El chip de fechas sale del mismo formateador que la fila de la agenda de +// /events, para que las dos digan exactamente lo mismo ("Aug 8 – 28"). +const range = spanLabel({ date: dates.start, until: dates.close, type: 'event', title: '' }, lang); + +// La rejilla del calendario del mes de construcción, marcada día a día. +const month = calendarMonth(dates); +// Huecos para cerrar la última semana: sin ellos la rejilla acaba en un +// escalón y el borde inferior deja de ser una línea. +const trail = (7 - ((month.lead + month.cells.length) % 7)) % 7; + +const communityHref = withLang('/community', lang); +const projectsHref = withLang('/projects', lang); + +// El botón de inscripción solo existe si hay formulario al otro lado. Mientras +// `form.url` esté vacío, la página dice cuándo abre en vez de enseñar un botón +// que no lleva a ningún sitio. +const formUrl = tt.form.url; +const signupOpen = state.phase === 'pre' || state.phase === 'live'; + +// CTA principal por fase. En `pre` y `live` empuja al formulario; después, al +// sitio donde la cosa ocurre de verdad. +const cta = + signupOpen && formUrl + ? { href: formUrl, label: tt.form.cta, external: true } + : state.phase === 'voting' + ? { href: communityHref, label: tt.ctaVote, external: false } + : state.phase === 'closed' + ? { href: projectsHref, label: tt.ctaClosed, external: false } + : { href: communityHref, label: tt.ctaSecondary, external: false }; +--- + + + + + +
    +
    +

    {tt.heroEyebrow}

    +

    {tt.heroTitle1}
    {tt.heroTitle2}

    +

    {tt.heroSub}

    + +
    + {status} + {range} · 2026 +
    + + + + {signupOpen && formUrl &&

    {tt.form.note}

    } +
    +
    + + +
    +
    +
    + 01{tt.idea.label} +
    +

    {tt.idea.title}

    +

    {tt.idea.sub}

    + +
      + {tt.idea.steps.map((s) => ( +
    1. + {s.n} +

      {s.name}

      +

      {s.body}

      +
    2. + ))} +
    + +

    {tt.idea.quote}

    +

    {tt.idea.note}

    +
    +
    + + +
    +
    +
    + 02{tt.prompt.label} +
    +

    {tt.prompt.title}

    +

    {tt.prompt.lead}

    + + {/* El prompt anotado a mano, como la captura de la que salió: el texto en + la columna del medio y las lecturas de cada trozo subrayado en los + márgenes. Cada párrafo es una fila propia para que su nota caiga a su + altura sin posicionar nada en absoluto; en móvil las notas se apilan + debajo del párrafo que anotan, que es donde tienen sentido. */} +
    +

    {tt.prompt.frame}

    + +
    +
    +
    +

    {P[0].map((s) => (s.ink ? {s.t} : s.t))}

    +
    +

    {tt.prompt.notes.ref}

    +
    +
    + +
    +
    +

    {tt.prompt.notes.loop}

    +

    {tt.prompt.notes.critic}

    +
    +

    {P[1].map((s) => (s.ink ? {s.t} : s.t))}

    +
    +

    {tt.prompt.notes.fanout}

    +

    {tt.prompt.notes.harsh}

    +
    +
    + +
    +
    +

    {P[2].map((s) => (s.ink ? {s.t} : s.t))}

    +
    +

    {tt.prompt.notes.ultra}

    +
    +
    +
    + +
    + {/* El texto que se copia sale de la misma lista que se pinta arriba, + así que no pueden divergir. El botón no habla con nadie: escribe + en el portapapeles y ya. */} + +

    {tt.prompt.foot}

    +
    +
    +
    +
    + + +
    +
    +
    + 03{tt.design.label} +
    +

    {tt.design.title}

    +

    {tt.design.sub}

    + +
    +
    +

    {tt.design.parallelTitle}

    +
      + {tt.design.parallelItems.map((i) =>
    • {i}
    • )} +
    +

    {tt.design.parallelBody}

    +
    +
    +

    {tt.design.ownerTitle}

    +
      + {tt.design.ownerItems.map((i) =>
    • {i}
    • )} +
    +

    {tt.design.ownerBody}

    +
    +
    + +

    {tt.design.quote}

    +
    +
    + + +
    +
    +
    + 04{tt.calendar.label} +
    +

    {tt.calendar.title}

    + + {/* Rejilla del mes entero: los días apagados también se pintan, para que + se vea de un vistazo cuánto ocupa el challenge dentro de agosto. Cada + día marcado lleva escrito lo que pasa ese día, así que el color es un + refuerzo y no la única fuente de información. */} +
    +

    {tt.calendar.month}

    + + +
    + +

    {tt.calendar.tbd}

    +
    +
    + + +
    +
    +
    + 05{tt.join.label} +
    +

    {tt.join.title}

    +

    {tt.join.sub}

    + +
      + {tt.join.steps.map((s, i) => ( +
    1. + {String(i + 1).padStart(2, '0')} + {s} +
    2. + ))} +
    + +
    +

    {tt.join.reqTitle}

    +
      + {tt.join.reqs.map((r) =>
    • {r}
    • )} +
    +
    + +
    +

    {tt.join.signupTitle}

    +

    {tt.join.signupBody}

    + + {formUrl ? ( + <> + + {tt.form.cta} → + +

    {tt.form.note}

    + + ) : ( +

    {tt.form.soonTitle} — {tt.form.soon}

    + )} +
    +
    +
    + + +
    +
    +
    + 06{tt.submit.label} +
    +

    {tt.submit.title}

    +

    {tt.submit.sub}

    + +

    {tt.submit.requiredLabel}

    + {/* Los dos últimos van destacados: en la fuente son los que prueban qué se + construyó y cómo, y por eso cierran la rejilla. */} +
      + {tt.submit.required.map((f, i) => ( +
    • = tt.submit.required.length - 2 }]}>{f}
    • + ))} +
    +

    {tt.submit.requiredNote}

    + +

    {tt.submit.recommendedLabel}

    +
      + {tt.submit.recommended.map((r) => ( +
    • + {r.name} + {r.why} +
    • + ))} +
    + +

    {tt.submit.rule}

    + +
      + {tt.submit.notes.map((n) =>
    • {n}
    • )} +
    + +
    +

    {tt.submit.whereTitle}

    +

    {tt.submit.whereBody}

    +
    +
    +
    + + +
    +
    +
    + 07{tt.after.label} +
    +

    {tt.after.title}

    +

    {tt.after.sub}

    + +
      + {tt.after.steps.map((s) => ( +
    1. + {s.when} +

      {s.name}

      +

      {s.body}

      +
    2. + ))} +
    + +
    +

    {tt.after.tbdLabel}

    +

    {tt.after.tbdBody}

    +
    + + {/* Sin botón de votar: la votación no está construida. El enlace lleva a + donde va a pasar de verdad. */} +

    {tt.after.note}

    + {tt.ctaVote} → +
    +
    + + +
    +
    +

    {tt.close.title}

    +

    {tt.close.text}

    + +
    +
    +
    + + + + diff --git a/src/pages/es/gauntlet.astro b/src/pages/es/gauntlet.astro new file mode 100644 index 0000000..9f53b60 --- /dev/null +++ b/src/pages/es/gauntlet.astro @@ -0,0 +1,8 @@ +--- +// Envoltorio de ruta: el cuerpo vive en el fichero _ vecino, fuera del árbol +// de rutas. Una página que importa otra página pierde la atribución del CSS y +// la ruta se sirve sin (lo guarda routeWrappers.test.ts). +import Page from '../_gauntlet.astro'; +--- + + diff --git a/src/pages/gauntlet.astro b/src/pages/gauntlet.astro new file mode 100644 index 0000000..fef305f --- /dev/null +++ b/src/pages/gauntlet.astro @@ -0,0 +1,8 @@ +--- +// Envoltorio de ruta: el cuerpo vive en el fichero _ vecino, fuera del árbol +// de rutas. Una página que importa otra página pierde la atribución del CSS y +// la ruta se sirve sin (lo guarda routeWrappers.test.ts). +import Page from './_gauntlet.astro'; +--- + + diff --git a/src/pages/sitemap.xml.ts b/src/pages/sitemap.xml.ts index 3f67cae..dd9a250 100644 --- a/src/pages/sitemap.xml.ts +++ b/src/pages/sitemap.xml.ts @@ -16,7 +16,16 @@ export const prerender = false; const SITE = 'https://nan.builders'; /** Rutas con versión en los dos idiomas. */ -const BILINGUAL = ['/', '/events', '/projects', '/community', '/privacy', '/terms', '/cookies']; +const BILINGUAL = [ + '/', + '/events', + '/gauntlet', + '/projects', + '/community', + '/privacy', + '/terms', + '/cookies', +]; /** * Fuera del sitemap a propósito: diff --git a/src/tests/landing/eventsData.test.ts b/src/tests/landing/eventsData.test.ts index 168b4be..c2099de 100644 --- a/src/tests/landing/eventsData.test.ts +++ b/src/tests/landing/eventsData.test.ts @@ -54,6 +54,16 @@ describe('agenda de eventos.json', () => { const key = `nan.events.types.${a.type}`; expect(t(key, lang)).not.toBe(key); } + if (a.href !== undefined) { + // Ruta interna y sin prefijo de idioma: la fila la compone con + // `withLang`, así que un '/es/...' aquí daría '/es/es/...'. Y tiene que + // existir la página, o la fila enlaza a un 404. + expect(a.href.startsWith('/')).toBe(true); + expect(a.href.startsWith('/es/')).toBe(false); + expect(a.href.endsWith('/')).toBe(false); + const route = resolve(here, `../../pages${a.href}.astro`); + expect(existsSync(route), `no existe ${a.href}`).toBe(true); + } if (a.until !== undefined) { expect(a.until).toMatch(ISO); // Inclusivo y hacia delante: un `until` anterior al inicio no marcaría @@ -88,6 +98,13 @@ describe('página de eventos', () => { expect(page).not.toContain('a.date === iso'); }); + test('las entradas con landing propia se pintan como enlace', () => { + // La fila era texto muerto: la entrada natural a la landing de un evento + // es su propia fila de agenda. + expect(page).toContain('a.href'); + expect(page).toContain('withLang(a.href, lang)'); + }); + test('los textos de las tarjetas pasan por el resolutor de idioma', () => { expect(page).toContain('{loc(v.title)}'); expect(page).toContain('{loc(v.desc)}'); diff --git a/src/tests/landing/gauntletPage.test.ts b/src/tests/landing/gauntletPage.test.ts new file mode 100644 index 0000000..d876005 --- /dev/null +++ b/src/tests/landing/gauntletPage.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, test } from 'vitest'; +import { existsSync, readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; +import eventos from '../../data/eventos.json'; +import { tObj } from '../../lib/i18n'; + +/** + * La landing del Gauntlet es una PÁGINA DE ANUNCIO: no guarda un solo dato. + * Esa decisión no se ve en el marcado —una isla o un formulario entran sin + * romper nada— así que se guarda aquí. Si algún día hay backend y se construye + * la entrega o la votación, estos tests son los que hay que cambiar a + * conciencia, no de pasada. + */ + +const here = dirname(fileURLToPath(import.meta.url)); +const pagesDir = resolve(here, '../../pages'); +const page = readFileSync(resolve(pagesDir, '_gauntlet.astro'), 'utf-8'); + +const ISO = /^\d{4}-\d{2}-\d{2}$/; + +describe('fechas del gauntlet en eventos.json', () => { + const g = eventos.gauntlet; + + test('están todas y en formato ISO', () => { + for (const key of ['workshop', 'start', 'close', 'votingEnd'] as const) { + expect(g[key]).toMatch(ISO); + } + }); + + test('van en orden', () => { + expect(g.workshop < g.start).toBe(true); + expect(g.start < g.close).toBe(true); + // votingEnd es provisional ("unos días" desde el 28), pero nunca puede caer + // antes del cierre: la fase `voting` no existiría. + expect(g.close <= g.votingEnd).toBe(true); + }); + + test('la entrada de la agenda cubre exactamente la ventana de construcción', () => { + const row = (eventos.agenda ?? []).find((a) => a.href === '/gauntlet'); + expect(row).toBeDefined(); + expect(row!.date).toBe(g.start); + expect(row!.until).toBe(g.close); + }); +}); + +describe('la landing no guarda datos', () => { + test('no monta ninguna isla', () => { + // Sin isla no hay estado de cliente que persistir. + expect(page).not.toMatch(/client:(load|idle|visible|only|media)/); + }); + + test('no hay formulario propio: la inscripción es un Google Form externo', () => { + expect(page).not.toContain(' { + expect(page).not.toContain('fetch('); + expect(page).not.toContain('/api/'); + }); +}); + +describe('enlaces de la landing', () => { + test('lo que se abre fuera lleva rel="noopener"', () => { + // El único salto de dominio es el Google Form. + const external = page.includes("target: '_blank'") || page.includes('target="_blank"'); + expect(external).toBe(true); + expect(page).toContain("rel: 'noopener'"); + }); + + test('las rutas internas pasan por withLang, para no perder el idioma', () => { + expect(page).toContain('withLang('); + // Enlaces a /community o /projects escritos a pelo se quedarían en inglés + // desde la versión española. + expect(page).not.toMatch(/href="\/(community|projects|events)"/); + }); +}); + +describe('copy del gauntlet', () => { + for (const lang of ['en', 'es'] as const) { + test(`la URL del formulario en ${lang} es el Google Form publicado`, () => { + const url = tObj<{ url: string }>('nan.gauntlet.form', lang).url; + // El CTA principal de la landing. Tiene que ser el enlace público del + // formulario: `/viewform` a secas, sin el `?usp=publish-editor` que + // añade el editor de Google al copiarlo —ese parámetro no rompe nada, + // pero delata de dónde salió y no pinta en una URL que se comparte. + expect(url.startsWith('https://docs.google.com/forms/')).toBe(true); + expect(url.endsWith('/viewform')).toBe(true); + }); + + test(`la entrega en ${lang} lista los 8 campos obligatorios`, () => { + const submit = tObj<{ required: string[] }>('nan.gauntlet.submit', lang); + expect(submit.required).toHaveLength(8); + }); + } +}); + +describe('rutas', () => { + test('existen los dos envoltorios', () => { + expect(existsSync(resolve(pagesDir, 'gauntlet.astro'))).toBe(true); + expect(existsSync(resolve(pagesDir, 'es/gauntlet.astro'))).toBe(true); + }); +}); diff --git a/src/tests/landing/gauntletPrompt.test.ts b/src/tests/landing/gauntletPrompt.test.ts new file mode 100644 index 0000000..130788a --- /dev/null +++ b/src/tests/landing/gauntletPrompt.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, test } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; +import { GAUNTLET_PROMPT, promptText, type PromptInk } from '../../data/gauntletPrompt'; +import { tObj } from '../../lib/i18n'; + +/** + * Lo que se copia tiene que ser el prompt original, palabra por palabra. Es un + * documento, no copy de la web: si alguien "mejora" una frase al retocar los + * subrayados, lo que se lleva la gente deja de ser el original. Por eso el + * texto entero está aquí escrito a mano, y no derivado de la fuente. + */ +const ORIGINAL = `I want you to build a first-person shooter at the level of the most recent Call of Duty games. It should be utterly perfect, visually beautiful, with every single thing done at AAA quality—from textures to physics to anything you could think of. + +Fan out sub-agents and have sub-agents tackle each one individually so that the game is utterly perfect. You should /loop on each item and have a separate sub-agent check it visually to ensure it looks triple A. That separate sub-agent should be a really harsh critic, and if it doesn't look triple A, it should keep going. + +Don't stop until each sub-agent is utterly wowed with the quality when compared with the actual Call of Duty game. It should literally compare them side by side blind and say which one looks better. Do this in ThreeJS. /loop until it's utterly perfect. Fan out sub-agents and ultracode.`; + +const here = dirname(fileURLToPath(import.meta.url)); +const page = readFileSync(resolve(here, '../../pages/_gauntlet.astro'), 'utf-8'); + +describe('el prompt que se copia', () => { + test('es el original, sin una coma de más', () => { + expect(promptText()).toBe(ORIGINAL); + }); + + test('los párrafos van separados por una línea en blanco', () => { + // Pegado en un chat, el prompt tiene que conservar sus tres bloques. + expect(promptText().split('\n\n')).toHaveLength(3); + expect(promptText()).not.toContain('\n\n\n'); + }); + + test('ningún trozo se queda pegado al siguiente', () => { + // El texto se parte para poder subrayar frases sueltas; un espacio perdido + // en una costura sale directo en el portapapeles. + expect(promptText()).not.toMatch(/ {2}/); + for (const paragraph of GAUNTLET_PROMPT) { + for (const piece of paragraph) expect(piece.t).not.toBe(''); + } + }); +}); + +describe('las anotaciones al margen', () => { + const inks = GAUNTLET_PROMPT.flat() + .map((p) => p.ink) + .filter((ink): ink is PromptInk => Boolean(ink)); + + test('cada color de tinta se usa una sola vez', () => { + // Dos subrayados del mismo color mandarían al lector a la misma nota desde + // dos sitios distintos. + expect(new Set(inks).size).toBe(inks.length); + }); + + for (const lang of ['en', 'es'] as const) { + test(`en ${lang} cada nota tiene su subrayado en el texto`, () => { + const notes = tObj<{ notes: Record }>('nan.gauntlet.prompt', lang).notes; + // `stack` (ThreeJS) va subrayado pero sin nota: es un dato, no una + // decisión que explicar. El resto tiene que existir en los dos sitios. + const annotated = inks.filter((ink) => ink !== 'stack'); + expect(Object.keys(notes).sort()).toEqual([...annotated].sort()); + }); + } +}); + +describe('el botón de copiar no habla con nadie', () => { + test('copia del portapapeles y punto', () => { + expect(page).toContain('navigator.clipboard.writeText'); + // Ya lo cubre gauntletPage.test.ts para la página entera, pero esta es la + // única pieza con JavaScript propio: si algún día alguien la usa para + // contar copias, el fallo tiene que salir aquí. + expect(page).not.toContain('navigator.sendBeacon'); + expect(page).not.toContain('XMLHttpRequest'); + }); + + test('el texto copiado sale de la misma fuente que el texto pintado', () => { + // Un literal escrito a mano en el `data-prompt` es exactamente la forma de + // que lo que se lee y lo que se copia acaben diciendo cosas distintas. + expect(page).toContain('data-prompt={promptText()}'); + }); +}); diff --git a/src/tests/landing/i18nParity.test.ts b/src/tests/landing/i18nParity.test.ts new file mode 100644 index 0000000..ffd933a --- /dev/null +++ b/src/tests/landing/i18nParity.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, test } from 'vitest'; +import en from '../../../i18n/en.json'; +import es from '../../../i18n/es.json'; + +/** + * Los dos diccionarios tienen que tener exactamente la misma forma. + * + * `useT` devuelve el subárbol `nan` tipado a partir de en.json, así que una + * clave que solo exista en inglés compila igual y sale `undefined` en pantalla + * —un hueco vacío, sin error—, y una lista con un elemento de menos deja media + * sección sin pintar. El copy diverge en silencio: aquí se caza. + */ + +type Node = unknown; + +function shapeDiffs(a: Node, b: Node, path: string, out: string[]): void { + if (Array.isArray(a) || Array.isArray(b)) { + if (!Array.isArray(a) || !Array.isArray(b)) return void out.push(`${path}: uno es lista y el otro no`); + if (a.length !== b.length) return void out.push(`${path}: ${a.length} elementos en en, ${b.length} en es`); + a.forEach((item, i) => shapeDiffs(item, b[i], `${path}[${i}]`, out)); + return; + } + + if (a && typeof a === 'object') { + if (!b || typeof b !== 'object') return void out.push(`${path}: uno es objeto y el otro no`); + const objA = a as Record; + const objB = b as Record; + for (const key of Object.keys(objA)) { + if (key in objB) shapeDiffs(objA[key], objB[key], `${path}.${key}`, out); + else out.push(`${path}.${key}: falta en es`); + } + for (const key of Object.keys(objB)) if (!(key in objA)) out.push(`${path}.${key}: falta en en`); + return; + } + + if (typeof a !== typeof b) out.push(`${path}: ${typeof a} en en, ${typeof b} en es`); +} + +/** Todas las cadenas del árbol, con su ruta. */ +function strings(node: Node, path: string, out: [string, string][] = []): [string, string][] { + if (typeof node === 'string') out.push([path, node]); + else if (Array.isArray(node)) node.forEach((item, i) => strings(item, `${path}[${i}]`, out)); + else if (node && typeof node === 'object') { + for (const [key, value] of Object.entries(node as Record)) { + strings(value, `${path}.${key}`, out); + } + } + return out; +} + +describe('paridad de los diccionarios', () => { + test('en.json y es.json tienen la misma forma bajo `nan`', () => { + const diffs: string[] = []; + shapeDiffs(en.nan, es.nan, 'nan', diffs); + expect(diffs).toEqual([]); + }); + + test('ninguna cadena está vacía', () => { + for (const [dict, name] of [ + [en.nan, 'en'], + [es.nan, 'es'], + ] as const) { + for (const [path, value] of strings(dict, 'nan')) { + expect(value.trim(), `${name}: ${path}`).not.toBe(''); + } + } + }); +}); diff --git a/src/tests/lib/gauntlet.test.ts b/src/tests/lib/gauntlet.test.ts new file mode 100644 index 0000000..7b9feaa --- /dev/null +++ b/src/tests/lib/gauntlet.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, test } from 'vitest'; +import { calendarMonth, daysBetween, gauntletState, statusText, type GauntletDates } from '../../lib/gauntlet'; + +/** + * La fase del challenge se calcula con la fecha del servidor, así que los + * errores que importan son los de los bordes: el día del cierre tiene que + * seguir siendo "se puede entregar", no "cerrado". Con el reloj como parámetro + * se puede fijar cada uno de esos días. + */ + +const DATES: GauntletDates = { + workshop: '2026-08-07', + start: '2026-08-08', + close: '2026-08-28', + votingEnd: '2026-08-31', +}; + +/** Fecha local, que es con la que trabajan fmtISO/parseISO. */ +const on = (iso: string) => { + const [y, m, d] = iso.split('-').map(Number); + return new Date(y, m - 1, d, 12, 0, 0); +}; + +describe('gauntletState', () => { + test('antes del arranque va en pre, con los días que faltan', () => { + expect(gauntletState(DATES, on('2026-07-20'))).toEqual({ phase: 'pre', days: 19 }); + }); + + test('el día del workshop sigue siendo pre: el challenge abre al día siguiente', () => { + expect(gauntletState(DATES, on('2026-08-07'))).toEqual({ phase: 'pre', days: 1 }); + }); + + test('el primer día de construcción ya es live', () => { + expect(gauntletState(DATES, on('2026-08-08'))).toEqual({ phase: 'live', days: 20 }); + }); + + test('el día del cierre TODAVÍA es live: se entrega hasta el final del día', () => { + // Es el borde que importa. Un `<` en vez de `<=` aquí cerraría la + // inscripción una jornada antes de lo anunciado. + expect(gauntletState(DATES, on('2026-08-28'))).toEqual({ phase: 'live', days: 0 }); + }); + + test('el día siguiente al cierre abre la votación', () => { + expect(gauntletState(DATES, on('2026-08-29')).phase).toBe('voting'); + }); + + test('el último día de votación sigue en votación', () => { + expect(gauntletState(DATES, on('2026-08-31')).phase).toBe('voting'); + }); + + test('pasada la votación, cerrado', () => { + expect(gauntletState(DATES, on('2026-09-01')).phase).toBe('closed'); + }); + + test('votación y cierre no llevan cuenta atrás', () => { + // La votación no tiene final anunciado ("unos días"), así que no se puede + // prometer un contador. + expect(gauntletState(DATES, on('2026-08-30')).days).toBe(0); + expect(gauntletState(DATES, on('2026-09-10')).days).toBe(0); + }); + + test('el cambio de mes no descuadra la cuenta', () => { + expect(daysBetween('2026-07-31', '2026-08-08')).toBe(8); + expect(daysBetween('2026-08-08', '2026-07-31')).toBe(-8); + }); +}); + +describe('calendarMonth', () => { + const month = calendarMonth(DATES); + const kind = (day: number) => month.cells[day - 1].kind; + + test('pinta el mes entero, no solo la ventana del challenge', () => { + expect(month.cells).toHaveLength(31); + expect(month.cells[0]).toEqual({ day: 1, iso: '2026-08-01', kind: 'idle' }); + }); + + test('la semana empieza en lunes: el 1 de agosto de 2026 cae en sábado', () => { + // Con la semana empezando en domingo el lead sería 6 y el mes entero + // saldría corrido una casilla. Es el fallo típico de una rejilla de mes. + expect(month.lead).toBe(5); + }); + + test('cada día lleva lo que pasa ese día', () => { + expect(kind(6)).toBe('idle'); + expect(kind(7)).toBe('workshop'); + expect(kind(8)).toBe('build'); + expect(kind(27)).toBe('build'); + // El 28 es cierre, no construcción: es el hito, y se pinta distinto. + expect(kind(28)).toBe('close'); + expect(kind(29)).toBe('voting'); + expect(kind(31)).toBe('voting'); + }); + + test('el mes sale de las fechas, no de un literal de agosto', () => { + const sept = calendarMonth({ + workshop: '2026-08-31', + start: '2026-09-01', + close: '2026-09-20', + votingEnd: '2026-09-24', + }); + expect(sept.cells).toHaveLength(30); + expect(sept.cells[0].iso).toBe('2026-09-01'); + // El workshop cae fuera del mes que se pinta: ningún día lo reclama. + expect(sept.cells.some((c) => c.kind === 'workshop')).toBe(false); + }); +}); + +describe('statusText', () => { + const strings = { + pre: 'empieza en {n} días', + preOne: 'empieza mañana', + preZero: 'empieza hoy', + live: 'quedan {n} días', + liveOne: 'mañana es el último día', + liveZero: 'último día', + voting: 'votaciones abiertas', + closed: 'edición cerrada', + }; + + test('interpola los días cuando faltan varios', () => { + expect(statusText({ phase: 'pre', days: 19 }, strings)).toBe('empieza en 19 días'); + expect(statusText({ phase: 'live', days: 20 }, strings)).toBe('quedan 20 días'); + }); + + test('hoy y mañana tienen frase propia: "en 0 días" no lo dice nadie', () => { + expect(statusText({ phase: 'pre', days: 0 }, strings)).toBe('empieza hoy'); + expect(statusText({ phase: 'pre', days: 1 }, strings)).toBe('empieza mañana'); + expect(statusText({ phase: 'live', days: 0 }, strings)).toBe('último día'); + expect(statusText({ phase: 'live', days: 1 }, strings)).toBe('mañana es el último día'); + }); + + test('votación y cierre son frases fijas', () => { + expect(statusText({ phase: 'voting', days: 0 }, strings)).toBe('votaciones abiertas'); + expect(statusText({ phase: 'closed', days: 0 }, strings)).toBe('edición cerrada'); + }); + + test('no deja plantillas sin sustituir', () => { + for (const phase of ['pre', 'live'] as const) { + for (const days of [0, 1, 2, 30]) { + expect(statusText({ phase, days }, strings)).not.toContain('{n}'); + } + } + }); +}); diff --git a/src/tests/lib/sitemap.test.ts b/src/tests/lib/sitemap.test.ts index 0264f90..ab02d4e 100644 --- a/src/tests/lib/sitemap.test.ts +++ b/src/tests/lib/sitemap.test.ts @@ -42,7 +42,7 @@ describe('sitemap.xml', () => { const { xml } = await sitemap(); const urls = locs(xml); - for (const path of ['/events', '/projects', '/community', '/privacy', '/terms', '/cookies']) { + for (const path of ['/events', '/gauntlet', '/projects', '/community', '/privacy', '/terms', '/cookies']) { expect(urls).toContain(`${SITE}${path}`); expect(urls).toContain(`${SITE}/es${path}`); } @@ -50,6 +50,13 @@ describe('sitemap.xml', () => { expect(urls).toContain(`${SITE}/es`); }); + test('la landing del gauntlet está declarada: se enlaza desde /events', async () => { + // Regla del proyecto: o entra en el sitemap, o va con noindex. La fila de + // la agenda la enlaza, así que tiene que entrar. + const { xml } = await sitemap(); + expect(locs(xml)).toContain(`${SITE}/gauntlet`); + }); + test('el hackatón queda fuera entero, en los dos idiomas', async () => { const { xml } = await sitemap(); const urls = locs(xml); From 6b0007ec40b3aea181de28e6bd17489aefe2f2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20G=C3=B3mez=20Jim=C3=A9nez?= Date: Sat, 8 Aug 2026 16:26:06 +0200 Subject: [PATCH 2/4] =?UTF-8?q?feat(gauntlet):=20una=20secci=C3=B3n=20de?= =?UTF-8?q?=20contexto=20para=20quien=20llega=20sin=20saber=20nada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La página pasaba del hero a "Un prompt no hace el trabajo", que es una lección sobre bucles servida a alguien que todavía no sabe qué se le está proponiendo. Y daba por sabido lo que nunca explicaba: qué es NaN —citado tres veces, incluido como requisito para participar—, de dónde sale el nombre, y que el tema es libre, que vivía en el pie de la sección del prompt detrás de 850 caracteres sobre Call of Duty. La nueva 01 va en dos velocidades: la historia del experimento como titular, para quien lee, y tres respuestas en rejilla —qué construyo, quién lo organiza, qué me llevo— para quien escanea. Todo lo demás baja un número. Dos cosas que la sección NO dice, a propósito: que ser miembro sea gratis (no lo es: el premio de un hackatón es "1 mes gratis de NaN") y que haya premio, porque no hay ninguno definido. La descripción de NaN es literalmente la de /community, para que la comunidad se cuente igual en toda la web. Va también un test que comprueba que los números de sección van seguidos: están escritos a mano y un 02 repetido no rompe nada, así que no se ve hasta producción. Co-Authored-By: Claude Opus 5 --- i18n/en.json | 20 +++++ i18n/es.json | 20 +++++ src/pages/_gauntlet.astro | 100 +++++++++++++++++++------ src/tests/landing/gauntletPage.test.ts | 13 ++++ 4 files changed, 132 insertions(+), 21 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 125be6f..1ccba99 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -548,6 +548,26 @@ "soonTitle": "signup opens at the kickoff", "soon": "The form goes live with the workshop on August 7. We announce the link on NaN's Discord." }, + "context": { + "label": "where this comes from", + "title": "Someone asked an agent for a Call of Duty.", + "lead": "Not once. They built a loop: sub-agents each owning one piece, and a critic comparing the result against the real game that refused to sign off until you couldn't tell them apart. That is a Gauntlet Loop, and it's what the workshop opening August takes apart. The challenge is that everyone builds their own.", + "cards": [ + { + "q": "What do I build?", + "a": "Whatever you want. The theme is open — the shooter was the experiment, not the assignment. What gets shown is your loop and what came out of it." + }, + { + "q": "Who runs it?", + "a": "NaN, the springboard for builders shipping with open AI. Taking part means being a member.", + "link": "How to join" + }, + { + "q": "What do I get?", + "a": "A finished project, in front of people. No jury: the community votes on what was submitted and the most voted present it live." + } + ] + }, "idea": { "label": "the key idea", "title": "A prompt doesn't do the work.", diff --git a/i18n/es.json b/i18n/es.json index 4afec49..c37ac13 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -548,6 +548,26 @@ "soonTitle": "la inscripción abre en el kickoff", "soon": "El formulario se publica con el workshop del 7 de agosto. El enlace se anuncia en el Discord de NaN." }, + "context": { + "label": "de dónde sale esto", + "title": "Alguien le pidió a un agente un Call of Duty.", + "lead": "No se lo pidió una vez. Montó un bucle: subagentes construyendo una pieza cada uno, y un crítico comparando el resultado con el juego real que no daba el visto bueno hasta que no se distinguía. Eso es un Gauntlet Loop, y es lo que desmonta el workshop que abre agosto. El reto es que cada uno monte el suyo.", + "cards": [ + { + "q": "¿Qué construyo?", + "a": "Lo que quieras. El tema es libre — el shooter era el experimento, no el encargo. Lo que se enseña es tu bucle y lo que salió de él." + }, + { + "q": "¿Quién lo organiza?", + "a": "NaN, el trampolín para builders que construyen con IA abierta. Participar pide ser miembro.", + "link": "Cómo se entra" + }, + { + "q": "¿Qué me llevo?", + "a": "Un proyecto terminado y delante de gente. Sin jurado: la comunidad vota lo entregado y los más votados lo presentan en directo." + } + ] + }, "idea": { "label": "la idea clave", "title": "Un prompt no hace el trabajo.", diff --git a/src/pages/_gauntlet.astro b/src/pages/_gauntlet.astro index 300075b..45a99ec 100644 --- a/src/pages/_gauntlet.astro +++ b/src/pages/_gauntlet.astro @@ -98,11 +98,38 @@ const cta = - + + {/* Lo primero después del hero, y lo único escrito para quien no sabe nada: + de dónde viene el nombre y las tres preguntas que hace cualquiera antes + de decidir si esto va con él. Todo lo que viene detrás da por sabido lo + que se cuenta aquí. */}
    - 01{tt.idea.label} + 01{tt.context.label} +
    +

    {tt.context.title}

    +

    {tt.context.lead}

    + +
    + {tt.context.cards.map((c) => ( +
    +

    {c.q}

    +

    {c.a}

    + {/* Solo la tarjeta de la comunidad tiene a dónde ir. Las otras dos + se responden aquí mismo y no mandan a nadie a ningún sitio. */} + {'link' in c && {c.link} →} +
    + ))} +
    +
    +
    + + +
    +
    +
    + 02{tt.idea.label}

    {tt.idea.title}

    {tt.idea.sub}

    @@ -122,11 +149,11 @@ const cta =
    - +
    - 02{tt.prompt.label} + 03{tt.prompt.label}

    {tt.prompt.title}

    {tt.prompt.lead}

    @@ -190,11 +217,11 @@ const cta =
    - +
    - 03{tt.design.label} + 04{tt.design.label}

    {tt.design.title}

    {tt.design.sub}

    @@ -220,11 +247,11 @@ const cta =
    - +
    - 04{tt.calendar.label} + 05{tt.calendar.label}

    {tt.calendar.title}

    @@ -252,11 +279,11 @@ const cta =
    - +
    - 05{tt.join.label} + 06{tt.join.label}

    {tt.join.title}

    {tt.join.sub}

    @@ -295,11 +322,11 @@ const cta =
    - +
    - 06{tt.submit.label} + 07{tt.submit.label}

    {tt.submit.title}

    {tt.submit.sub}

    @@ -337,11 +364,11 @@ const cta =
    - +
    - 07{tt.after.label} + 08{tt.after.label}

    {tt.after.title}

    {tt.after.sub}

    @@ -444,7 +471,35 @@ const cta = } .glink:hover { color: var(--color-text); border-bottom-color: var(--color-text); } - /* ---------- 01 el loop: tira de 5 celdas, como la del calendario ---------- */ + /* ---------- 01 las tres preguntas ---------- + Misma rejilla de hairlines que el resto: tres celdas para las tres cosas + que hay que saber antes de seguir leyendo. */ + .qa { + margin-top: var(--space-10); + display: grid; grid-template-columns: repeat(3, 1fr); + gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); + } + .qa__card { + background: var(--color-bg); padding: clamp(20px, 2.4vw, 32px); + display: flex; flex-direction: column; + transition: background var(--dur-ui) var(--ease); + } + .qa__card:hover { background: var(--color-surface); } + .qa__q { + font-family: var(--font-serif); font-weight: 400; color: var(--color-violet-2); + font-size: clamp(18px, 1.7vw, 23px); line-height: 1.15; + } + .qa__a { margin-top: var(--space-4); color: var(--color-text); font-size: 15px; line-height: 1.65; } + /* Anclado abajo: con respuestas de distinto largo, el enlace de la tercera + tarjeta se quedaba a media altura y parecía de otra fila. */ + .qa__link { + margin-top: auto; padding-top: var(--space-5); align-self: flex-start; + font-size: 12px; color: var(--color-link); + transition: color var(--dur-micro) var(--ease); + } + .qa__link:hover { color: var(--color-text); } + + /* ---------- 02 el loop: tira de 5 celdas, como la del calendario ---------- */ .loop { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); @@ -462,7 +517,7 @@ const cta = } .loop__body { margin-top: var(--space-3); color: var(--color-muted); font-size: 14px; line-height: 1.6; } - /* ---------- 02 dos columnas enfrentadas ---------- */ + /* ---------- 04 dos columnas enfrentadas ---------- */ .split { margin-top: var(--space-10); display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); @@ -480,7 +535,7 @@ const cta = } .split__body { margin-top: var(--space-5); color: var(--color-muted); font-size: 15px; line-height: 1.65; } - /* ---------- 02 el prompt anotado ---------- + /* ---------- 03 el prompt anotado ---------- Una hoja con anotaciones al margen. Los seis colores de tinta son locales a esta sección a propósito: el sistema es violeta de arriba abajo, y aquí lo que se está reproduciendo es una captura llena de rotulador. Fuera de este @@ -555,7 +610,7 @@ const cta = .copy[data-state='done'] { background: var(--color-violet); color: #fff; } .copy__note { font-size: 12px; color: var(--color-muted); max-width: 46ch; } - /* ---------- 04 calendario del mes ---------- + /* ---------- 05 calendario del mes ---------- Rejilla de 7 columnas con las hairlines de 1px del sistema: el fondo del contenedor se ve a través de los huecos del `gap`, que es como está hecha la tira del loop y la rejilla de campos. @@ -613,7 +668,7 @@ const cta = .cal__day.is-voting { background: color-mix(in srgb, var(--color-violet) 22%, transparent); } .cal__day.is-voting .cal__n { color: var(--color-text); } - /* ---------- 04 pasos + paneles ---------- */ + /* ---------- 06 pasos + paneles ---------- */ .steps { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); @@ -646,7 +701,7 @@ const cta = width: 7px; height: 7px; background: var(--color-violet); } - /* ---------- 06 entrega ---------- */ + /* ---------- 07 entrega ---------- */ .fields { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; @@ -675,7 +730,7 @@ const cta = } .fine li::before { content: ''; position: absolute; left: 0; top: 0.7em; width: 8px; height: 1px; background: var(--color-muted); } - /* ---------- 06 después del cierre ---------- */ + /* ---------- 08 después del cierre ---------- */ .after { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); @@ -709,6 +764,9 @@ const cta = } @media (max-width: 900px) { .after { grid-template-columns: 1fr; } + /* Con dos columnas la tercera pregunta se queda sola en su fila y la + rejilla se lee rota; apiladas siguen siendo tres respuestas seguidas. */ + .qa { grid-template-columns: 1fr; } .split { grid-template-columns: 1fr; } .recs__row { grid-template-columns: 160px 1fr; } /* Sin márgenes que den para una nota, cada anotación baja debajo del diff --git a/src/tests/landing/gauntletPage.test.ts b/src/tests/landing/gauntletPage.test.ts index d876005..a95ce29 100644 --- a/src/tests/landing/gauntletPage.test.ts +++ b/src/tests/landing/gauntletPage.test.ts @@ -96,6 +96,19 @@ describe('copy del gauntlet', () => { } }); +describe('la numeración de las secciones', () => { + const numbers = [...page.matchAll(/class="sechead__n">(\d\d) m[1]); + + test('van seguidas desde 01, sin saltos ni repetidos', () => { + // Los números están escritos a mano en la plantilla, así que meter una + // sección por el medio obliga a renumerar todas las de abajo. Un 02 + // repetido no rompe nada —la página se pinta igual— y no lo ve nadie + // hasta que está en producción. + expect(numbers.length).toBeGreaterThan(0); + expect(numbers).toEqual(numbers.map((_, i) => String(i + 1).padStart(2, '0'))); + }); +}); + describe('rutas', () => { test('existen los dos envoltorios', () => { expect(existsSync(resolve(pagesDir, 'gauntlet.astro'))).toBe(true); From 754bc77bbd5cedfe6484656ac34234d286e8f346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20G=C3=B3mez=20Jim=C3=A9nez?= Date: Sun, 9 Aug 2026 14:13:52 +0200 Subject: [PATCH 3/4] =?UTF-8?q?feat(gauntlet):=20el=20prompt=20sube=20a=20?= =?UTF-8?q?la=2002=20y=20la=2001=20ense=C3=B1a=20lo=20que=20ya=20ha=20sali?= =?UTF-8?q?do?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La landing explicaba el Gauntlet Loop antes de enseñar nada. Quien llega no sabe lo que es un bucle de agentes, pero sí sabe reconocer un juego que funciona, así que la primera sección pasa a ser cinco proyectos publicados en las últimas semanas y la explicación viene detrás. Son obra de terceros, y eso manda en cómo se muestran: - Cada tarjeta enlaza a la publicación original y lleva el handle a la vista. - La miniatura es el fotograma de portada, servido desde /public. La página no tiene JavaScript de terceros y no iba a estrenarlo ahora: mirar esta sección no manda una sola petición a X hasta que alguien hace clic. - Solo lleva la etiqueta "cita el Gauntlet Loop" el único autor que lo nombra. El título de la sección habla de lo que está saliendo, no de la técnica, porque decir que los cinco salen del bucle sería firmar por los otros cuatro. Lo que no se traduce (enlace, imagen, autor, fecha) vive en data/, y el título y la descripción en i18n, emparejados por id. De paso, el prompt sube por delante de la idea, y el último guión largo visible de la landing pasa a ser dos puntos. Co-Authored-By: Claude Opus 5 --- i18n/en.json | 77 +++-- i18n/es.json | 73 +++-- public/img/gauntlet/anatomy-atelier.webp | Bin 0 -> 17810 bytes public/img/gauntlet/ironveil-range.webp | Bin 0 -> 5158 bytes public/img/gauntlet/modern-claudefare.webp | Bin 0 -> 61384 bytes public/img/gauntlet/neural-museum.webp | Bin 0 -> 13538 bytes public/img/gauntlet/shooter-original.webp | Bin 0 -> 20084 bytes src/data/gauntletShowcase.ts | 75 +++++ src/pages/_gauntlet.astro | 320 +++++++++++++++------ src/tests/landing/gauntletPage.test.ts | 47 +++ 10 files changed, 464 insertions(+), 128 deletions(-) create mode 100644 public/img/gauntlet/anatomy-atelier.webp create mode 100644 public/img/gauntlet/ironveil-range.webp create mode 100644 public/img/gauntlet/modern-claudefare.webp create mode 100644 public/img/gauntlet/neural-museum.webp create mode 100644 public/img/gauntlet/shooter-original.webp create mode 100644 src/data/gauntletShowcase.ts diff --git a/i18n/en.json b/i18n/en.json index 1ccba99..c6701a4 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -521,12 +521,12 @@ } }, "gauntlet": { - "metaTitle": "Gauntlet Challenge — NaN", + "metaTitle": "Gauntlet Challenge · NaN", "metaDesc": "August at NaN: one prompt fires up a system that builds, critiques and corrects itself. Build until August 28, then the community votes.", "heroEyebrow": "// gauntlet challenge", "heroTitle1": "Gauntlet", "heroTitle2": "Challenge.", - "heroSub": "One prompt. A system that builds, critiques and corrects itself. Build until August 28 — then the community votes.", + "heroSub": "One prompt. A system that builds, critiques and corrects itself. Build until August 28. Then the community votes.", "status": { "pre": "Starts in {n} days", "preOne": "Starts tomorrow", @@ -548,6 +548,35 @@ "soonTitle": "signup opens at the kickoff", "soon": "The form goes live with the workshop on August 7. We announce the link on NaN's Discord." }, + "showcase": { + "label": "what's already shipping", + "title": "This is what's coming out of building this way.", + "lead": "None of this is ours. These are projects other people posted over the last few weeks: every card goes to the original post, which is where the video lives.", + "credits": "credits the Gauntlet Loop", + "foot": "All posted between July 25 and August 8, 2026.", + "items": { + "claudefare": { + "title": "Modern Claudefare", + "desc": "A Call of Duty remake with four maps, solo and multiplayer, and 84,100 lines of code. Its author says they put it together in a few days applying the principles of the Gauntlet Loop." + }, + "original": { + "title": "The original", + "desc": "The shooter that started all of this. Not one external asset: every line is custom." + }, + "museum": { + "title": "The Neural Museum", + "desc": "A museum you walk through where every framed piece is an AI paper." + }, + "anatomy": { + "title": "Anatomy Atelier", + "desc": "Human anatomy in 3D, organ by organ, inside the browser." + }, + "ironveil": { + "title": "Ironveil Range", + "desc": "A playable ballistic missile defense simulator." + } + } + }, "context": { "label": "where this comes from", "title": "Someone asked an agent for a Call of Duty.", @@ -555,7 +584,7 @@ "cards": [ { "q": "What do I build?", - "a": "Whatever you want. The theme is open — the shooter was the experiment, not the assignment. What gets shown is your loop and what came out of it." + "a": "Whatever you want. The theme is open: the shooter was the experiment, not the assignment. What gets shown is your loop and what came out of it." }, { "q": "Who runs it?", @@ -568,6 +597,24 @@ } ] }, + "prompt": { + "label": "the prompt", + "title": "This is the whole prompt. Copy it and make it yours.", + "lead": "Here it is in full, annotated with what each piece is doing. Copy it and swap in your own thing: what carries over isn't the shooter, it's the shape.", + "frame": "Gauntlet Prompt", + "copy": "Copy the prompt", + "copied": "Copied", + "copyFail": "Select it and copy it by hand", + "foot": "Copied verbatim, in English: it is the original. Yours does not have to be a shooter. The theme is open.", + "notes": { + "ref": "Reference", + "fanout": "Fan out sub-agents, each owning one element until it is perfect", + "loop": "Every sub-agent inside a loop", + "critic": "One critic sub-agent per builder sub-agent", + "harsh": "Be a harsh critic and do not stop until it is indistinguishable from the original", + "ultra": "Super saiyan 4, ultra instinct, sharingan, find the one piece" + } + }, "idea": { "label": "the key idea", "title": "A prompt doesn't do the work.", @@ -600,25 +647,7 @@ } ], "quote": "The edge isn't thinking harder. It's closing the loop with feedback you can observe.", - "note": "\"One prompt\" means one initial human instruction — not one call, and not one agent." - }, - "prompt": { - "label": "the prompt", - "title": "This is the whole prompt. Copy it and make it yours.", - "lead": "The one from the experiment that started all this. There is no trick underneath: one instruction, sub-agents that own a piece each, and a critic that refuses to sign off until the thing survives the comparison.", - "frame": "Gauntlet Prompt", - "copy": "Copy the prompt", - "copied": "Copied", - "copyFail": "Select it and copy it by hand", - "foot": "Copied verbatim, in English: it is the original. Yours does not have to be a shooter — the theme is open.", - "notes": { - "ref": "Reference", - "fanout": "Fan out sub-agents, each owning one element until it is perfect", - "loop": "Every sub-agent inside a loop", - "critic": "One critic sub-agent per builder sub-agent", - "harsh": "Be a harsh critic and do not stop until it is indistinguishable from the original", - "ultra": "Super saiyan 4, ultra instinct, sharingan, find the one piece" - } + "note": "\"One prompt\" means one initial human instruction, not one call and not one agent." }, "design": { "label": "how it's designed", @@ -680,7 +709,7 @@ "Submit before August 28" ], "signupTitle": "signup is always open", - "signupBody": "There's no early cut-off: the form stays open until closing day. Nobody is left out for showing up late — the calendar is there to spread the work, not to shut a door." + "signupBody": "There's no early cut-off: the form stays open until closing day. Nobody is left out for showing up late. The calendar is there to spread the work, not to shut a door." }, "submit": { "label": "the submission", @@ -727,7 +756,7 @@ ], "rule": "the image is required · the video is recommended · a public demo is optional", "notes": [ - "Repository: it can stay private during August, but it has to be opened at closing time — or shared with the organisers so it can be verified.", + "Repository: it can stay private during August, but it has to be opened at closing time, or shared with the organisers so it can be verified.", "Video: we don't host it. A YouTube, Loom or similar URL works." ], "whereTitle": "where to submit", diff --git a/i18n/es.json b/i18n/es.json index c37ac13..4f02e56 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -521,7 +521,7 @@ } }, "gauntlet": { - "metaTitle": "Gauntlet Challenge — NaN", + "metaTitle": "Gauntlet Challenge · NaN", "metaDesc": "Agosto en NaN: un prompt activa un sistema que construye, critica y se corrige solo. Construye hasta el 28 de agosto y después vota la comunidad.", "heroEyebrow": "// gauntlet challenge", "heroTitle1": "Gauntlet", @@ -548,6 +548,35 @@ "soonTitle": "la inscripción abre en el kickoff", "soon": "El formulario se publica con el workshop del 7 de agosto. El enlace se anuncia en el Discord de NaN." }, + "showcase": { + "label": "lo que ya está saliendo", + "title": "Esto es lo que está saliendo de construir así.", + "lead": "Nada de esto es nuestro. Son proyectos que ha publicado otra gente en las últimas semanas: cada tarjeta lleva a la publicación original, que es donde está el vídeo.", + "credits": "cita el Gauntlet Loop", + "foot": "Todos publicados entre el 25 de julio y el 8 de agosto de 2026.", + "items": { + "claudefare": { + "title": "Modern Claudefare", + "desc": "Un remake de Call of Duty con cuatro mapas, modo solo y multijugador, y 84.100 líneas de código. Su autor cuenta que lo levantó en unos días aplicando los principios del Gauntlet Loop." + }, + "original": { + "title": "El original", + "desc": "El shooter que empezó todo. Ni un solo asset externo: todo el código es propio." + }, + "museum": { + "title": "The Neural Museum", + "desc": "Un museo que se recorre y en el que cada cuadro es un paper de IA." + }, + "anatomy": { + "title": "Anatomy Atelier", + "desc": "Anatomía humana en 3D, órgano a órgano, dentro del navegador." + }, + "ironveil": { + "title": "Ironveil Range", + "desc": "Un simulador de defensa antimisiles, jugable." + } + } + }, "context": { "label": "de dónde sale esto", "title": "Alguien le pidió a un agente un Call of Duty.", @@ -555,7 +584,7 @@ "cards": [ { "q": "¿Qué construyo?", - "a": "Lo que quieras. El tema es libre — el shooter era el experimento, no el encargo. Lo que se enseña es tu bucle y lo que salió de él." + "a": "Lo que quieras. El tema es libre: el shooter era el experimento, no el encargo. Lo que se enseña es tu bucle y lo que salió de él." }, { "q": "¿Quién lo organiza?", @@ -568,6 +597,24 @@ } ] }, + "prompt": { + "label": "el prompt", + "title": "Este es el prompt entero. Cópialo y adáptalo.", + "lead": "Aquí está entero, con las anotaciones de lo que hace cada trozo. Cópialo y cámbialo por lo tuyo: lo que se aprovecha no es el shooter, es la forma.", + "frame": "Gauntlet Prompt", + "copy": "Copiar el prompt", + "copied": "Copiado", + "copyFail": "Selecciónalo y cópialo a mano", + "foot": "Se copia tal cual, en inglés: es el original. El tuyo no tiene por qué ser un shooter. El tema es libre.", + "notes": { + "ref": "Referencia", + "fanout": "Despliega subagentes que se encarguen individualmente de cada elemento para que quede perfecto", + "loop": "Cada subagente dentro de un loop", + "critic": "Un subagente crítico por cada agente constructor", + "harsh": "Sé muy crítico y no pares hasta que no se distinga la versión original", + "ultra": "Super saiyan 4, ultrainstinto, sharingan, encuentra el one piece" + } + }, "idea": { "label": "la idea clave", "title": "Un prompt no hace el trabajo.", @@ -602,24 +649,6 @@ "quote": "La ventaja no está en pensar más, sino en cerrar el bucle con feedback observable.", "note": "«Un prompt» es una instrucción humana inicial, no una sola llamada ni un único agente." }, - "prompt": { - "label": "el prompt", - "title": "Este es el prompt entero. Cópialo y adáptalo.", - "lead": "El del experimento que dio pie a todo esto. Debajo no hay más truco: una instrucción, subagentes que se llevan una pieza cada uno y un crítico que no da el visto bueno hasta que la cosa aguanta la comparación.", - "frame": "Gauntlet Prompt", - "copy": "Copiar el prompt", - "copied": "Copiado", - "copyFail": "Selecciónalo y cópialo a mano", - "foot": "Se copia tal cual, en inglés: es el original. El tuyo no tiene por qué ser un shooter — el tema es libre.", - "notes": { - "ref": "Referencia", - "fanout": "Despliega subagentes que se encarguen individualmente de cada elemento para que quede perfecto", - "loop": "Cada subagente dentro de un loop", - "critic": "Un subagente crítico por cada agente constructor", - "harsh": "Sé muy crítico y no pares hasta que no se distinga la versión original", - "ultra": "Super saiyan 4, ultrainstinto, sharingan, encuentra el one piece" - } - }, "design": { "label": "cómo se diseña", "title": "Más agentes no significa mejores resultados.", @@ -680,7 +709,7 @@ "Entregar antes del 28 de agosto" ], "signupTitle": "la inscripción está siempre abierta", - "signupBody": "No hay ventana que se cierre antes: el formulario sigue abierto hasta el día del cierre. Nadie se queda fuera por llegar tarde — el calendario está para repartir el trabajo, no para levantar una puerta." + "signupBody": "No hay ventana que se cierre antes: el formulario sigue abierto hasta el día del cierre. Nadie se queda fuera por llegar tarde. El calendario está para repartir el trabajo, no para levantar una puerta." }, "submit": { "label": "la entrega", @@ -727,7 +756,7 @@ ], "rule": "la imagen es obligatoria · el vídeo es recomendado · la demo pública es opcional", "notes": [ - "Repositorio: puede seguir privado durante agosto, pero tiene que abrirse al cierre — o compartirse con la organización para poder verificarlo.", + "Repositorio: puede seguir privado durante agosto, pero tiene que abrirse al cierre, o compartirse con la organización para poder verificarlo.", "Vídeo: no lo alojamos. Vale una URL de YouTube, Loom o similares." ], "whereTitle": "por dónde se entrega", diff --git a/public/img/gauntlet/anatomy-atelier.webp b/public/img/gauntlet/anatomy-atelier.webp new file mode 100644 index 0000000000000000000000000000000000000000..b7fa4b8c6c2e6f94f9182028ad234e73cf6dc27a GIT binary patch literal 17810 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 literal 0 HcmV?d00001 diff --git a/public/img/gauntlet/ironveil-range.webp b/public/img/gauntlet/ironveil-range.webp new file mode 100644 index 0000000000000000000000000000000000000000..4dea1380b04049dffb723f8a9678f961dc232eee GIT binary patch literal 5158 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

    dW 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 literal 0 HcmV?d00001 diff --git a/public/img/gauntlet/neural-museum.webp b/public/img/gauntlet/neural-museum.webp new file mode 100644 index 0000000000000000000000000000000000000000..6fbafbab4af4fe983f234ba718d978e3d2122a4c GIT binary patch literal 13538 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 literal 0 HcmV?d00001 diff --git a/public/img/gauntlet/shooter-original.webp b/public/img/gauntlet/shooter-original.webp new file mode 100644 index 0000000000000000000000000000000000000000..c671cc11ab00e04136fddf38d2467fc4ec6999ee GIT binary patch literal 20084 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 (tt.showcase.items as Record)[id]; + // El botón de inscripción solo existe si hay formulario al otro lado. Mientras // `form.url` esté vacío, la página dice cuándo abre en vez de enseñar un botón // que no lleva a ningún sitio. @@ -98,15 +106,85 @@ const cta =

    - - {/* Lo primero después del hero, y lo único escrito para quien no sabe nada: - de dónde viene el nombre y las tres preguntas que hace cualquiera antes - de decidir si esto va con él. Todo lo que viene detrás da por sabido lo - que se cuenta aquí. */} + + {/* Enseñar antes de explicar: quien llega no sabe qué es un Gauntlet Loop, + pero sí sabe reconocer un juego que funciona. Son cinco proyectos de otra + gente, así que cada tarjeta enlaza a su publicación y lleva el handle a la + vista. Las miniaturas se sirven desde /public a propósito: abrir la página + no manda una sola petición a X hasta que alguien hace clic. */}
    +
    + + + {/* Lo único escrito para quien no sabe nada: de dónde viene el nombre y las + tres preguntas que hace cualquiera antes de decidir si esto va con él. + Todo lo que viene detrás da por sabido lo que se cuenta aquí. */} +
    +
    +
    + 02{tt.context.label}

    {tt.context.title}

    {tt.context.lead}

    @@ -125,30 +203,6 @@ const cta =
    - -
    -
    -
    - 02{tt.idea.label} -
    -

    {tt.idea.title}

    -

    {tt.idea.sub}

    - -
      - {tt.idea.steps.map((s) => ( -
    1. - {s.n} -

      {s.name}

      -

      {s.body}

      -
    2. - ))} -
    - -

    {tt.idea.quote}

    -

    {tt.idea.note}

    -
    -
    -
    @@ -217,11 +271,35 @@ const cta =
    - + +
    +
    +
    + 04{tt.idea.label} +
    +

    {tt.idea.title}

    +

    {tt.idea.sub}

    + +
      + {tt.idea.steps.map((s) => ( +
    1. + {s.n} +

      {s.name}

      +

      {s.body}

      +
    2. + ))} +
    + +

    {tt.idea.quote}

    +

    {tt.idea.note}

    +
    +
    + +
    - 04{tt.design.label} + 05{tt.design.label}

    {tt.design.title}

    {tt.design.sub}

    @@ -247,11 +325,11 @@ const cta =
    - +
    - 05{tt.calendar.label} + 06{tt.calendar.label}

    {tt.calendar.title}

    @@ -279,11 +357,11 @@ const cta =
    - +
    - 06{tt.join.label} + 07{tt.join.label}

    {tt.join.title}

    {tt.join.sub}

    @@ -316,17 +394,17 @@ const cta =

    {tt.form.note}

    ) : ( -

    {tt.form.soonTitle} — {tt.form.soon}

    +

    {tt.form.soonTitle}: {tt.form.soon}

    )}
    - +
    - 07{tt.submit.label} + 08{tt.submit.label}

    {tt.submit.title}

    {tt.submit.sub}

    @@ -364,11 +442,11 @@ const cta =
    - +
    - 08{tt.after.label} + 09{tt.after.label}

    {tt.after.title}

    {tt.after.sub}

    @@ -471,7 +549,78 @@ const cta = } .glink:hover { color: var(--color-text); border-bottom-color: var(--color-text); } - /* ---------- 01 las tres preguntas ---------- + /* ---------- 01 los ejemplos ---------- + Misma pantalla que las tarjetas de vídeo de /events (marco, líneas de + barrido, triángulo de play): son vídeos, y el usuario ya sabe en esta web + lo que significa esa caja. La diferencia es que estas SÍ son clicables, + porque el vídeo está en otro sitio. */ + .shot { display: block; color: inherit; } + .shot__screen { + position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden; + background: + radial-gradient(120% 140% at 50% 0%, rgba(125,57,235,0.22), transparent 60%), + var(--color-surface); + border: 1px solid var(--color-line); + transition: border-color var(--dur-ui) var(--ease); + } + .shot:hover .shot__screen { border-color: color-mix(in srgb, var(--color-violet) 55%, transparent); } + .shot__img { + position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; + filter: brightness(0.82) contrast(1.02); + transition: filter var(--dur-ui) var(--ease), transform var(--dur-ui) var(--ease); + } + .shot:hover .shot__img { filter: brightness(1); transform: scale(1.03); } + .shot__scan { + position: absolute; inset: 0; opacity: 0.5; + background: repeating-linear-gradient(0deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 3px); + } + .shot__play { + position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); + width: 0; height: 0; border-left: 20px solid var(--color-violet-2); + border-top: 12px solid transparent; border-bottom: 12px solid transparent; + filter: drop-shadow(0 0 12px rgba(125,57,235,0.5)); + transition: transform var(--dur-ui) var(--ease); + } + .shot:hover .shot__play { transform: translate(-50%, -50%) scale(1.12); } + .shot__badge { + position: absolute; left: 8px; top: 8px; font-size: 10px; letter-spacing: 0.1em; + text-transform: uppercase; color: #fff; background: var(--color-violet); padding: 3px 7px; + } + .shot__body { display: block; } + .shot__title { + display: block; margin-top: var(--space-4); + font-family: var(--font-serif); font-weight: 400; color: var(--color-text); + font-size: clamp(19px, 1.6vw, 24px); line-height: 1.1; + } + .shot__desc { + display: block; margin-top: var(--space-3); + color: var(--color-muted); font-size: 15px; line-height: 1.55; + } + /* El handle no es un adorno: es de quien es cada cosa. Por eso va siempre + visible y no aparece solo al pasar por encima. */ + .shot__meta { + display: block; margin-top: var(--space-3); + font-size: 12px; letter-spacing: 0.02em; color: var(--color-violet-2); + transition: color var(--dur-micro) var(--ease); + } + .shot:hover .shot__meta { color: var(--color-text); } + + /* El destacado: la pantalla a la izquierda y el texto al lado, para que se + lea como una portada y no como una tarjeta más grande. */ + .shot--lead { + margin-top: var(--space-10); + display: grid; grid-template-columns: 1.35fr 1fr; + gap: var(--space-8); align-items: center; + } + .shot--lead .shot__title { margin-top: 0; font-size: clamp(26px, 2.6vw, 40px); } + .shot--lead .shot__desc { font-size: 16px; max-width: 42ch; } + + .shots { + margin-top: var(--space-10); display: grid; + grid-template-columns: repeat(4, 1fr); gap: var(--space-8) var(--space-6); + } + + /* ---------- 02 las tres preguntas ---------- Misma rejilla de hairlines que el resto: tres celdas para las tres cosas que hay que saber antes de seguir leyendo. */ .qa { @@ -499,42 +648,6 @@ const cta = } .qa__link:hover { color: var(--color-text); } - /* ---------- 02 el loop: tira de 5 celdas, como la del calendario ---------- */ - .loop { - list-style: none; margin: var(--space-10) 0 0; padding: 0; - display: grid; grid-template-columns: repeat(5, 1fr); - gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); - } - .loop__step { - background: var(--color-bg); padding: var(--space-6); - transition: background var(--dur-ui) var(--ease); - } - .loop__step:hover { background: var(--color-surface); } - .loop__n { font-size: 12px; color: var(--color-violet-2); font-weight: 700; } - .loop__name { - margin-top: var(--space-3); font-family: var(--font-serif); font-weight: 400; - color: var(--color-text); font-size: clamp(19px, 1.8vw, 24px); line-height: 1.1; - } - .loop__body { margin-top: var(--space-3); color: var(--color-muted); font-size: 14px; line-height: 1.6; } - - /* ---------- 04 dos columnas enfrentadas ---------- */ - .split { - margin-top: var(--space-10); display: grid; grid-template-columns: 1fr 1fr; - gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); - } - .split__col { background: var(--color-bg); padding: clamp(24px, 3vw, 40px); } - .split__title { - font-family: var(--font-serif); font-weight: 400; color: var(--color-text); - font-size: clamp(20px, 2vw, 28px); line-height: 1.1; - } - .split__items { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; } - .split__items li { - font-size: 12px; color: var(--color-violet-2); padding: 4px 9px; - border: 1px solid color-mix(in srgb, var(--color-violet) 45%, transparent); - background: var(--color-violet-dim); - } - .split__body { margin-top: var(--space-5); color: var(--color-muted); font-size: 15px; line-height: 1.65; } - /* ---------- 03 el prompt anotado ---------- Una hoja con anotaciones al margen. Los seis colores de tinta son locales a esta sección a propósito: el sistema es violeta de arriba abajo, y aquí lo @@ -610,7 +723,43 @@ const cta = .copy[data-state='done'] { background: var(--color-violet); color: #fff; } .copy__note { font-size: 12px; color: var(--color-muted); max-width: 46ch; } - /* ---------- 05 calendario del mes ---------- + /* ---------- 04 el loop: tira de 5 celdas, como la del calendario ---------- */ + .loop { + list-style: none; margin: var(--space-10) 0 0; padding: 0; + display: grid; grid-template-columns: repeat(5, 1fr); + gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); + } + .loop__step { + background: var(--color-bg); padding: var(--space-6); + transition: background var(--dur-ui) var(--ease); + } + .loop__step:hover { background: var(--color-surface); } + .loop__n { font-size: 12px; color: var(--color-violet-2); font-weight: 700; } + .loop__name { + margin-top: var(--space-3); font-family: var(--font-serif); font-weight: 400; + color: var(--color-text); font-size: clamp(19px, 1.8vw, 24px); line-height: 1.1; + } + .loop__body { margin-top: var(--space-3); color: var(--color-muted); font-size: 14px; line-height: 1.6; } + + /* ---------- 05 dos columnas enfrentadas ---------- */ + .split { + margin-top: var(--space-10); display: grid; grid-template-columns: 1fr 1fr; + gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); + } + .split__col { background: var(--color-bg); padding: clamp(24px, 3vw, 40px); } + .split__title { + font-family: var(--font-serif); font-weight: 400; color: var(--color-text); + font-size: clamp(20px, 2vw, 28px); line-height: 1.1; + } + .split__items { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; } + .split__items li { + font-size: 12px; color: var(--color-violet-2); padding: 4px 9px; + border: 1px solid color-mix(in srgb, var(--color-violet) 45%, transparent); + background: var(--color-violet-dim); + } + .split__body { margin-top: var(--space-5); color: var(--color-muted); font-size: 15px; line-height: 1.65; } + + /* ---------- 06 calendario del mes ---------- Rejilla de 7 columnas con las hairlines de 1px del sistema: el fondo del contenedor se ve a través de los huecos del `gap`, que es como está hecha la tira del loop y la rejilla de campos. @@ -668,7 +817,7 @@ const cta = .cal__day.is-voting { background: color-mix(in srgb, var(--color-violet) 22%, transparent); } .cal__day.is-voting .cal__n { color: var(--color-text); } - /* ---------- 06 pasos + paneles ---------- */ + /* ---------- 07 pasos + paneles ---------- */ .steps { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); @@ -701,7 +850,7 @@ const cta = width: 7px; height: 7px; background: var(--color-violet); } - /* ---------- 07 entrega ---------- */ + /* ---------- 08 entrega ---------- */ .fields { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; @@ -730,7 +879,7 @@ const cta = } .fine li::before { content: ''; position: absolute; left: 0; top: 0.7em; width: 8px; height: 1px; background: var(--color-muted); } - /* ---------- 08 después del cierre ---------- */ + /* ---------- 09 después del cierre ---------- */ .after { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); @@ -764,6 +913,12 @@ const cta = } @media (max-width: 900px) { .after { grid-template-columns: 1fr; } + /* El destacado deja de estar enfrentado y pasa a ser portada + pie. En dos + columnas estrechas la descripción se rompía en renglones de tres + palabras al lado de una pantalla ya diminuta. */ + .shot--lead { grid-template-columns: 1fr; gap: 0; } + .shot--lead .shot__title { margin-top: var(--space-4); } + .shots { grid-template-columns: repeat(2, 1fr); } /* Con dos columnas la tercera pregunta se queda sola en su fila y la rejilla se lee rota; apiladas siguen siendo tres respuestas seguidas. */ .qa { grid-template-columns: 1fr; } @@ -790,6 +945,7 @@ const cta = } @media (max-width: 620px) { .loop, .steps { grid-template-columns: repeat(2, 1fr); } + .shots { grid-template-columns: 1fr; } .fields { grid-template-columns: 1fr; } .recs__row { grid-template-columns: 1fr; gap: var(--space-1); } .cal__month { padding-inline: var(--space-4); } diff --git a/src/tests/landing/gauntletPage.test.ts b/src/tests/landing/gauntletPage.test.ts index a95ce29..4faf964 100644 --- a/src/tests/landing/gauntletPage.test.ts +++ b/src/tests/landing/gauntletPage.test.ts @@ -3,6 +3,7 @@ import { existsSync, readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; import eventos from '../../data/eventos.json'; +import { GAUNTLET_SHOWCASE } from '../../data/gauntletShowcase'; import { tObj } from '../../lib/i18n'; /** @@ -96,6 +97,52 @@ describe('copy del gauntlet', () => { } }); +describe('los ejemplos de la 01', () => { + const publicDir = resolve(here, '../../../public'); + + test('cada ejemplo enlaza a la publicación original en x.com', () => { + // Son obra de terceros. El enlace al post es lo que convierte la miniatura + // en una cita y no en material apropiado, así que no puede faltar ni + // apuntar a otro sitio. + for (const s of GAUNTLET_SHOWCASE) { + expect(s.href).toMatch(/^https:\/\/x\.com\/[^/]+\/status\/\d+$/); + expect(s.href.toLowerCase()).toContain(`/${s.author.toLowerCase()}/`); + } + }); + + test('la miniatura de cada ejemplo está servida desde el repo', () => { + // Si se colase una URL de pbs.twimg.com, abrir la landing pasaría a mandar + // a cada visitante a los servidores de X sin que haya tocado nada. + for (const s of GAUNTLET_SHOWCASE) { + expect(s.img.startsWith('/img/gauntlet/')).toBe(true); + expect(existsSync(resolve(publicDir, s.img.slice(1)))).toBe(true); + } + }); + + test('solo lleva la etiqueta del bucle quien lo cita', () => { + // Cuatro de los cinco autores no nombran el Gauntlet Loop. Marcar uno de + // más es ponerle a alguien una frase que no ha dicho. + expect(GAUNTLET_SHOWCASE.filter((s) => s.credits)).toHaveLength(1); + expect(GAUNTLET_SHOWCASE[0].credits).toBe(true); + }); + + for (const lang of ['en', 'es'] as const) { + test(`en ${lang} cada ejemplo tiene título y descripción`, () => { + // El emparejamiento es por id y no lo comprueba el tipado: un id mal + // escrito en i18n deja la tarjeta con el texto en blanco. + const copy = tObj>( + 'nan.gauntlet.showcase.items', + lang, + ); + expect(Object.keys(copy)).toHaveLength(GAUNTLET_SHOWCASE.length); + for (const s of GAUNTLET_SHOWCASE) { + expect(copy[s.id]?.title?.length).toBeGreaterThan(0); + expect(copy[s.id]?.desc?.length).toBeGreaterThan(0); + } + }); + } +}); + describe('la numeración de las secciones', () => { const numbers = [...page.matchAll(/class="sechead__n">(\d\d) m[1]); From 6002308c36ee88419b170b93027ef66a8e89fe66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20G=C3=B3mez=20Jim=C3=A9nez?= Date: Sun, 9 Aug 2026 15:57:08 +0200 Subject: [PATCH 4/4] =?UTF-8?q?feat(gauntlet):=20el=20reto,=20arriba=20y?= =?UTF-8?q?=20en=20una=20sola=20secci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La pregunta "¿y yo qué tengo que hacer?" se contestaba en la 07 y la 08, detrás de cuatro secciones de explicación, y para cuando llegabas ya no lo tenías claro. Ahora es la 02, justo después de los ejemplos: qué se pide, el requisito y qué se entrega, todo en el mismo sitio. El título deja de ser un juego de palabras y dice lo que es: "El reto consiste en:". Desaparecen la lista de cinco pasos —que describía el proceso, no el encargo— y la sección de diseño entera, que no aportaba nada que no estuviera ya en el prompt. Con la 05 fuera y la 07 y la 08 fundidas, la página pasa de nueve secciones a siete. Se van también el CSS de .split y .steps, que ya no los pinta nadie. Co-Authored-By: Claude Opus 5 --- i18n/en.json | 53 ++---- i18n/es.json | 53 ++---- src/pages/_gauntlet.astro | 347 +++++++++++++++----------------------- 3 files changed, 161 insertions(+), 292 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index c6701a4..ab9fc1c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -577,6 +577,18 @@ } } }, + "join": { + "label": "the challenge", + "title": "The challenge is:", + "sub": "Build something good with the Gauntlet Loop prompt, using at least one NaN model. The theme is open: the shooter was the experiment, not the assignment.", + "reqTitle": "requirements", + "reqs": [ + "Use at least one NaN model" + ], + "deliverLabel": "what you hand in at the end", + "signupTitle": "signup is always open", + "signupBody": "There's no early cut-off: the form stays open until closing day. Nobody is left out for showing up late. The calendar is there to spread the work, not to shut a door." + }, "context": { "label": "where this comes from", "title": "Someone asked an agent for a Call of Duty.", @@ -649,26 +661,6 @@ "quote": "The edge isn't thinking harder. It's closing the loop with feedback you can observe.", "note": "\"One prompt\" means one initial human instruction, not one call and not one agent." }, - "design": { - "label": "how it's designed", - "title": "More agents doesn't mean better results.", - "sub": "The lesson from the experiment: what breaks a loop isn't the number of agents, it's who owns what.", - "parallelTitle": "Parallelise what's independent", - "parallelItems": [ - "Lighting", - "Materials", - "Colour" - ], - "parallelBody": "Pieces that don't touch each other can run at the same time without stepping on anyone.", - "ownerTitle": "One owner for what's coupled", - "ownerItems": [ - "Weapons", - "Sound", - "Enemies" - ], - "ownerBody": "Systems that depend on each other need a single hand, or they drift apart.", - "quote": "The Gauntlet isn't about spawning a hundred agents. It's about designing ownership well." - }, "calendar": { "label": "calendar", "title": "August is a season, not a day.", @@ -690,27 +682,6 @@ "voting": "Vote" } }, - "join": { - "label": "how to take part", - "title": "Build and publish. No permission needed.", - "sub": "You sign up, you build with what you already have on NaN and you publish. Nobody hands out slots.", - "steps": [ - "Find the challenge", - "Build", - "Publish", - "Update", - "Mark the final run" - ], - "reqTitle": "what you need", - "reqs": [ - "Be a NaN member", - "Use open models available on NaN", - "Document the loop and the human intervention", - "Submit before August 28" - ], - "signupTitle": "signup is always open", - "signupBody": "There's no early cut-off: the form stays open until closing day. Nobody is left out for showing up late. The calendar is there to spread the work, not to shut a door." - }, "submit": { "label": "the submission", "title": "What you submit has to prove what you built, and how.", diff --git a/i18n/es.json b/i18n/es.json index 4f02e56..85dfadc 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -577,6 +577,18 @@ } } }, + "join": { + "label": "el reto", + "title": "El reto consiste en:", + "sub": "Montar algo que esté guapo con el prompt del Gauntlet Loop, usando al menos un modelo de NaN. El tema es libre: el shooter era el experimento, no el encargo.", + "reqTitle": "requisitos", + "reqs": [ + "Usar al menos un modelo de NaN" + ], + "deliverLabel": "lo que se entrega al final", + "signupTitle": "la inscripción está siempre abierta", + "signupBody": "No hay ventana que se cierre antes: el formulario sigue abierto hasta el día del cierre. Nadie se queda fuera por llegar tarde. El calendario está para repartir el trabajo, no para levantar una puerta." + }, "context": { "label": "de dónde sale esto", "title": "Alguien le pidió a un agente un Call of Duty.", @@ -649,26 +661,6 @@ "quote": "La ventaja no está en pensar más, sino en cerrar el bucle con feedback observable.", "note": "«Un prompt» es una instrucción humana inicial, no una sola llamada ni un único agente." }, - "design": { - "label": "cómo se diseña", - "title": "Más agentes no significa mejores resultados.", - "sub": "La lección del experimento: lo que rompe un bucle no es el número de agentes, sino quién es dueño de qué.", - "parallelTitle": "Paraleliza lo independiente", - "parallelItems": [ - "Luz", - "Materiales", - "Color" - ], - "parallelBody": "Las piezas que no se tocan pueden ir a la vez sin pisarse.", - "ownerTitle": "Un solo responsable para lo acoplado", - "ownerItems": [ - "Armas", - "Sonido", - "Enemigos" - ], - "ownerBody": "Los sistemas que dependen entre sí necesitan una sola mano, o se separan.", - "quote": "El Gauntlet no es crear cien agentes: es diseñar bien la propiedad del trabajo." - }, "calendar": { "label": "calendario", "title": "Agosto es una temporada, no un día.", @@ -690,27 +682,6 @@ "voting": "Votar" } }, - "join": { - "label": "cómo participar", - "title": "Construye y publica. Sin pedir permiso.", - "sub": "Te apuntas, construyes con lo que ya tienes en NaN y publicas. Aquí nadie reparte plazas.", - "steps": [ - "Descubre el reto", - "Construye", - "Publica", - "Actualiza", - "Marca la ejecución final" - ], - "reqTitle": "lo que hace falta", - "reqs": [ - "Ser miembro de NaN", - "Usar modelos abiertos disponibles en NaN", - "Documentar el loop y la intervención humana", - "Entregar antes del 28 de agosto" - ], - "signupTitle": "la inscripción está siempre abierta", - "signupBody": "No hay ventana que se cierre antes: el formulario sigue abierto hasta el día del cierre. Nadie se queda fuera por llegar tarde. El calendario está para repartir el trabajo, no para levantar una puerta." - }, "submit": { "label": "la entrega", "title": "Lo que entregas tiene que probar qué construiste y cómo.", diff --git a/src/pages/_gauntlet.astro b/src/pages/_gauntlet.astro index 20c2f75..5e752f5 100644 --- a/src/pages/_gauntlet.astro +++ b/src/pages/_gauntlet.astro @@ -177,14 +177,88 @@ const cta =
    - + + {/* La sección que contesta "¿y yo qué tengo que hacer?", y va aquí arriba + justo por eso: antes estaba la séptima, detrás de cuatro secciones de + explicación, y para cuando llegabas ya no lo tenías claro. + + Todo lo que hace falta para participar vive en esta sección y en ninguna + otra: qué se pide, el requisito y qué se entrega. Lo de abajo es contexto + —de dónde sale, el prompt, la idea, las fechas— y se puede leer o no. */} +
    +
    +
    + 02{tt.join.label} +
    +

    {tt.join.title}

    +

    {tt.join.sub}

    + +
    +

    {tt.join.reqTitle}

    +
      + {tt.join.reqs.map((r) =>
    • {r}
    • )} +
    +
    + +

    {tt.join.deliverLabel}

    +

    {tt.submit.title} {tt.submit.sub}

    + + {/* Los dos últimos van destacados: en la fuente son los que prueban qué se + construyó y cómo, y por eso cierran la rejilla. */} +
      + {tt.submit.required.map((f, i) => ( +
    • = tt.submit.required.length - 2 }]}>{f}
    • + ))} +
    +

    {tt.submit.requiredNote}

    + +

    {tt.submit.recommendedLabel}

    +
      + {tt.submit.recommended.map((r) => ( +
    • + {r.name} + {r.why} +
    • + ))} +
    + +

    {tt.submit.rule}

    + +
      + {tt.submit.notes.map((n) =>
    • {n}
    • )} +
    + +
    +

    {tt.submit.whereTitle}

    +

    {tt.submit.whereBody}

    +
    + +
    +

    {tt.join.signupTitle}

    +

    {tt.join.signupBody}

    + + {formUrl ? ( + <> + + {tt.form.cta} → + +

    {tt.form.note}

    + + ) : ( +

    {tt.form.soonTitle}: {tt.form.soon}

    + )} +
    +
    +
    + + {/* Lo único escrito para quien no sabe nada: de dónde viene el nombre y las tres preguntas que hace cualquiera antes de decidir si esto va con él. Todo lo que viene detrás da por sabido lo que se cuenta aquí. */}
    - 02{tt.context.label} + 03{tt.context.label}

    {tt.context.title}

    {tt.context.lead}

    @@ -203,11 +277,11 @@ const cta =
    - +
    - 03{tt.prompt.label} + 04{tt.prompt.label}

    {tt.prompt.title}

    {tt.prompt.lead}

    @@ -271,11 +345,11 @@ const cta =
    - +
    - 04{tt.idea.label} + 05{tt.idea.label}

    {tt.idea.title}

    {tt.idea.sub}

    @@ -295,36 +369,6 @@ const cta =
    - -
    -
    -
    - 05{tt.design.label} -
    -

    {tt.design.title}

    -

    {tt.design.sub}

    - -
    -
    -

    {tt.design.parallelTitle}

    -
      - {tt.design.parallelItems.map((i) =>
    • {i}
    • )} -
    -

    {tt.design.parallelBody}

    -
    -
    -

    {tt.design.ownerTitle}

    -
      - {tt.design.ownerItems.map((i) =>
    • {i}
    • )} -
    -

    {tt.design.ownerBody}

    -
    -
    - -

    {tt.design.quote}

    -
    -
    -
    @@ -357,96 +401,11 @@ const cta =
    - -
    -
    -
    - 07{tt.join.label} -
    -

    {tt.join.title}

    -

    {tt.join.sub}

    - -
      - {tt.join.steps.map((s, i) => ( -
    1. - {String(i + 1).padStart(2, '0')} - {s} -
    2. - ))} -
    - -
    -

    {tt.join.reqTitle}

    -
      - {tt.join.reqs.map((r) =>
    • {r}
    • )} -
    -
    - -
    -

    {tt.join.signupTitle}

    -

    {tt.join.signupBody}

    - - {formUrl ? ( - <> - - {tt.form.cta} → - -

    {tt.form.note}

    - - ) : ( -

    {tt.form.soonTitle}: {tt.form.soon}

    - )} -
    -
    -
    - - +
    - 08{tt.submit.label} -
    -

    {tt.submit.title}

    -

    {tt.submit.sub}

    - -

    {tt.submit.requiredLabel}

    - {/* Los dos últimos van destacados: en la fuente son los que prueban qué se - construyó y cómo, y por eso cierran la rejilla. */} -
      - {tt.submit.required.map((f, i) => ( -
    • = tt.submit.required.length - 2 }]}>{f}
    • - ))} -
    -

    {tt.submit.requiredNote}

    - -

    {tt.submit.recommendedLabel}

    -
      - {tt.submit.recommended.map((r) => ( -
    • - {r.name} - {r.why} -
    • - ))} -
    - -

    {tt.submit.rule}

    - -
      - {tt.submit.notes.map((n) =>
    • {n}
    • )} -
    - -
    -

    {tt.submit.whereTitle}

    -

    {tt.submit.whereBody}

    -
    -
    -
    - - -
    -
    -
    - 09{tt.after.label} + 07{tt.after.label}

    {tt.after.title}

    {tt.after.sub}

    @@ -620,7 +579,56 @@ const cta = grid-template-columns: repeat(4, 1fr); gap: var(--space-8) var(--space-6); } - /* ---------- 02 las tres preguntas ---------- + /* ---------- 02 el reto: paneles, campos de la entrega y recomendados ---------- */ + .panel { margin-top: var(--space-8); border: 1px solid var(--color-line); padding: clamp(20px, 2.4vw, 32px); } + .panel--v { + border-color: color-mix(in srgb, var(--color-violet) 45%, transparent); + background: var(--color-violet-dim); + } + .panel__t { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-violet-2); } + .panel__b { margin-top: var(--space-4); max-width: 68ch; color: var(--color-text); font-size: 16px; line-height: 1.65; } + .panel__cta { margin-top: var(--space-6); } + .panel__note { margin-top: var(--space-4); font-size: 12px; line-height: 1.6; color: var(--color-muted); } + + .reqs { list-style: none; margin: var(--space-4) 0 0; padding: 0; } + .reqs__item { + position: relative; padding-left: 20px; margin-top: var(--space-3); + color: var(--color-text); font-size: 16px; line-height: 1.6; + } + .reqs__item::before { + content: ''; position: absolute; left: 0; top: 0.6em; + width: 7px; height: 7px; background: var(--color-violet); + } + + .fields { + list-style: none; margin: var(--space-5) 0 0; padding: 0; + display: grid; grid-template-columns: 1fr 1fr; + gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); + } + .field-item { + background: var(--color-bg); padding: var(--space-5); + font-family: var(--font-serif); font-size: clamp(16px, 1.4vw, 20px); + color: var(--color-text); line-height: 1.2; + } + .field-item.is-key { background: var(--color-violet-dim); color: var(--color-violet-2); } + + .recs { list-style: none; margin: var(--space-5) 0 0; padding: 0; border-top: 1px solid var(--color-line); } + .recs__row { + display: grid; grid-template-columns: 240px 1fr; gap: var(--space-4); + align-items: baseline; padding-block: var(--space-4); + border-bottom: 1px solid var(--color-line); + } + .recs__name { font-family: var(--font-serif); font-size: clamp(16px, 1.4vw, 20px); color: var(--color-text); } + .recs__why { color: var(--color-muted); font-size: 15px; line-height: 1.6; } + + .fine { list-style: none; margin: var(--space-6) 0 0; padding: 0; max-width: 72ch; } + .fine li { + position: relative; padding-left: 16px; margin-top: var(--space-3); + color: var(--color-muted); font-size: 14px; line-height: 1.65; + } + .fine li::before { content: ''; position: absolute; left: 0; top: 0.7em; width: 8px; height: 1px; background: var(--color-muted); } + + /* ---------- 03 las tres preguntas ---------- Misma rejilla de hairlines que el resto: tres celdas para las tres cosas que hay que saber antes de seguir leyendo. */ .qa { @@ -648,7 +656,7 @@ const cta = } .qa__link:hover { color: var(--color-text); } - /* ---------- 03 el prompt anotado ---------- + /* ---------- 04 el prompt anotado ---------- Una hoja con anotaciones al margen. Los seis colores de tinta son locales a esta sección a propósito: el sistema es violeta de arriba abajo, y aquí lo que se está reproduciendo es una captura llena de rotulador. Fuera de este @@ -723,7 +731,7 @@ const cta = .copy[data-state='done'] { background: var(--color-violet); color: #fff; } .copy__note { font-size: 12px; color: var(--color-muted); max-width: 46ch; } - /* ---------- 04 el loop: tira de 5 celdas, como la del calendario ---------- */ + /* ---------- 05 el loop: tira de 5 celdas, como la del calendario ---------- */ .loop { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); @@ -741,24 +749,6 @@ const cta = } .loop__body { margin-top: var(--space-3); color: var(--color-muted); font-size: 14px; line-height: 1.6; } - /* ---------- 05 dos columnas enfrentadas ---------- */ - .split { - margin-top: var(--space-10); display: grid; grid-template-columns: 1fr 1fr; - gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); - } - .split__col { background: var(--color-bg); padding: clamp(24px, 3vw, 40px); } - .split__title { - font-family: var(--font-serif); font-weight: 400; color: var(--color-text); - font-size: clamp(20px, 2vw, 28px); line-height: 1.1; - } - .split__items { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; } - .split__items li { - font-size: 12px; color: var(--color-violet-2); padding: 4px 9px; - border: 1px solid color-mix(in srgb, var(--color-violet) 45%, transparent); - background: var(--color-violet-dim); - } - .split__body { margin-top: var(--space-5); color: var(--color-muted); font-size: 15px; line-height: 1.65; } - /* ---------- 06 calendario del mes ---------- Rejilla de 7 columnas con las hairlines de 1px del sistema: el fondo del contenedor se ve a través de los huecos del `gap`, que es como está hecha @@ -817,69 +807,7 @@ const cta = .cal__day.is-voting { background: color-mix(in srgb, var(--color-violet) 22%, transparent); } .cal__day.is-voting .cal__n { color: var(--color-text); } - /* ---------- 07 pasos + paneles ---------- */ - .steps { - list-style: none; margin: var(--space-10) 0 0; padding: 0; - display: grid; grid-template-columns: repeat(5, 1fr); - gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); - } - .steps__item { - background: var(--color-bg); padding: var(--space-5); - display: flex; flex-direction: column; gap: var(--space-3); - } - .steps__n { font-size: 12px; color: var(--color-violet-2); font-weight: 700; } - .steps__label { font-family: var(--font-serif); font-size: clamp(16px, 1.4vw, 20px); color: var(--color-text); line-height: 1.15; } - - .panel { margin-top: var(--space-8); border: 1px solid var(--color-line); padding: clamp(20px, 2.4vw, 32px); } - .panel--v { - border-color: color-mix(in srgb, var(--color-violet) 45%, transparent); - background: var(--color-violet-dim); - } - .panel__t { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-violet-2); } - .panel__b { margin-top: var(--space-4); max-width: 68ch; color: var(--color-text); font-size: 16px; line-height: 1.65; } - .panel__cta { margin-top: var(--space-6); } - .panel__note { margin-top: var(--space-4); font-size: 12px; line-height: 1.6; color: var(--color-muted); } - - .reqs { list-style: none; margin: var(--space-4) 0 0; padding: 0; } - .reqs__item { - position: relative; padding-left: 20px; margin-top: var(--space-3); - color: var(--color-text); font-size: 16px; line-height: 1.6; - } - .reqs__item::before { - content: ''; position: absolute; left: 0; top: 0.6em; - width: 7px; height: 7px; background: var(--color-violet); - } - - /* ---------- 08 entrega ---------- */ - .fields { - list-style: none; margin: var(--space-5) 0 0; padding: 0; - display: grid; grid-template-columns: 1fr 1fr; - gap: 1px; background: var(--color-line); border: 1px solid var(--color-line); - } - .field-item { - background: var(--color-bg); padding: var(--space-5); - font-family: var(--font-serif); font-size: clamp(16px, 1.4vw, 20px); - color: var(--color-text); line-height: 1.2; - } - .field-item.is-key { background: var(--color-violet-dim); color: var(--color-violet-2); } - - .recs { list-style: none; margin: var(--space-5) 0 0; padding: 0; border-top: 1px solid var(--color-line); } - .recs__row { - display: grid; grid-template-columns: 240px 1fr; gap: var(--space-4); - align-items: baseline; padding-block: var(--space-4); - border-bottom: 1px solid var(--color-line); - } - .recs__name { font-family: var(--font-serif); font-size: clamp(16px, 1.4vw, 20px); color: var(--color-text); } - .recs__why { color: var(--color-muted); font-size: 15px; line-height: 1.6; } - - .fine { list-style: none; margin: var(--space-6) 0 0; padding: 0; max-width: 72ch; } - .fine li { - position: relative; padding-left: 16px; margin-top: var(--space-3); - color: var(--color-muted); font-size: 14px; line-height: 1.65; - } - .fine li::before { content: ''; position: absolute; left: 0; top: 0.7em; width: 8px; height: 1px; background: var(--color-muted); } - - /* ---------- 09 después del cierre ---------- */ + /* ---------- 07 después del cierre ---------- */ .after { list-style: none; margin: var(--space-10) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); @@ -909,7 +837,7 @@ const cta = reordena en ningún punto — siete columnas son siete días, y una semana partida deja de ser un calendario. */ @media (max-width: 1100px) { - .loop, .steps { grid-template-columns: repeat(3, 1fr); } + .loop { grid-template-columns: repeat(3, 1fr); } } @media (max-width: 900px) { .after { grid-template-columns: 1fr; } @@ -922,7 +850,6 @@ const cta = /* Con dos columnas la tercera pregunta se queda sola en su fila y la rejilla se lee rota; apiladas siguen siendo tres respuestas seguidas. */ .qa { grid-template-columns: 1fr; } - .split { grid-template-columns: 1fr; } .recs__row { grid-template-columns: 160px 1fr; } /* Sin márgenes que den para una nota, cada anotación baja debajo del párrafo que anota —que es el único sitio donde se sigue entendiendo a @@ -944,7 +871,7 @@ const cta = .note::before { content: '↳ '; opacity: 0.7; } } @media (max-width: 620px) { - .loop, .steps { grid-template-columns: repeat(2, 1fr); } + .loop { grid-template-columns: repeat(2, 1fr); } .shots { grid-template-columns: 1fr; } .fields { grid-template-columns: 1fr; } .recs__row { grid-template-columns: 1fr; gap: var(--space-1); }