diff --git a/.github/ISSUE_TEMPLATE/benchmark-case.yml b/.github/ISSUE_TEMPLATE/benchmark-case.yml index 79b8414..e02d31f 100644 --- a/.github/ISSUE_TEMPLATE/benchmark-case.yml +++ b/.github/ISSUE_TEMPLATE/benchmark-case.yml @@ -1,5 +1,5 @@ -name: Propose a benchmark case -description: Add a slide behavior that generated PowerPoint artifacts should prove. +name: Propose a challenge case +description: Add a native presentation behavior that generated artifacts should prove. title: "[case]: " labels: ["benchmark-case"] body: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ce6090..24ace50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,13 @@ # Contributing to Gloss -Gloss improves when a benchmark claim becomes easier to inspect, reproduce, or challenge. Small, evidence-backed pull requests are welcome. +Gloss improves when the challenge presentation becomes harder to fake and easier to +understand. Small, evidence-backed pull requests are welcome. ## Good first contributions -- Add a PowerPoint construct the corpus does not cover. +- Add a native presentation behavior the deck does not cover. - Reproduce a grader false positive or false negative. +- Make one of the twenty slide explanations or prompts clearer. - Improve a candidate assertion and its mutation fixture. - Improve a deterministic comparative generation path and prove the score change. - Review a prompt requirement, evidence record, or release gate. @@ -30,6 +32,7 @@ Install [uv](https://docs.astral.sh/uv/), Python 3.12, LibreOffice Impress, and git clone https://github.com/aronchick/gloss.git cd gloss/gloss-v1/grader uv sync --extra dev --locked +uv run gloss check ../benchmark/deck/gold/gloss-v1-gold.pptx uv run ../benchmark/validate_corpus.py uv run pytest tests/test_mutation_fixtures.py -q ``` @@ -57,9 +60,9 @@ uv run pytest --cov=gloss_service --cov-fail-under=85 uv run pip-audit ``` -## Adding a benchmark case +## Adding a challenge case -A benchmark change should identify: +A deck change should identify: - the natural-language requirement; - the intended visual and native artifact behavior; @@ -83,12 +86,11 @@ Keep a pull request focused enough to review. In the description, include: CI must pass. Maintainers may ask for a smaller fixture, clearer provenance, or an explicit release-state caveat before merging. -## Launch surface +## Website -The static site lives in `site/`. Harness counts come from -`site/evidence/preview-v1.json`; comparative bars come from the byte-equivalent -public copy of `gloss-v1/benchmark/comparative-v1/summary.json`. Verify both -sources, local assets, copy, and media metadata with: +The static site lives in `site/` and should lead with the real deck, the exact +prompt, and GitHub contribution paths. Verify links, assets, copy, evidence, and +media metadata with: ```bash node launch/verify-launch.mjs diff --git a/README.md b/README.md index d0298a5..7390b5c 100644 --- a/README.md +++ b/README.md @@ -1,211 +1,315 @@ # Gloss -**Gloss — Generative Layout & OOXML Scoring System** is an open, deliberately -difficult PowerPoint deck that AI struggles to render and build as a real, editable -artifact. Its accompanying measurement tools make the failure modes easy to inspect, -reproduce, and improve. +**A deliberately complicated presentation that AI struggles to make natively.** -The tools inspect both sides of a deck: +Gloss is an open, 20-slide challenge for presentation-generating AI. The goal is +not to make screenshots that happen to look like slides. The goal is to make a +real presentation: editable text, native charts and tables, working masters and +layouts, live fields, connected shapes, correct reading order, and intentional +grouping all the way down. -- **Looks right:** rendered pixels, typography, geometry, and visual consistency. -- **Built right:** native shapes, charts, tables, layouts, masters, text semantics, - relationships, and package safety. +The deck is the challenge. Gloss also includes a small checker that makes native +object changes easy to see, plus deeper measurement tools for people developing +generators and evaluation systems. -**[gloss.tools](https://gloss.tools)** · -[Issues](https://github.com/aronchick/gloss/issues) · -[Pull requests](https://github.com/aronchick/gloss/pulls) · -[Contributing](CONTRIBUTING.md) +The name expands to **Generative Layout & Object Structure Standard**: a reminder +that layout is visible, while object structure is what keeps a presentation real. -> **A screenshot is not a PowerPoint.** The artifact is the product. +**[Open gloss.tools](https://gloss.tools)** · +**[Download the deck](https://github.com/aronchick/gloss/raw/refs/heads/main/gloss-v1/benchmark/deck/gold/gloss-v1-gold.pptx)** · +**[Copy the exact prompt](gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md)** · +**[Join the work](https://github.com/aronchick/gloss/issues)** -## The ACID idea +> **A screenshot is not a presentation. The artifact is the product.** -The browser ACID tests made standards failures impossible to hand-wave away: every -browser received the same hostile, public artifact, and everyone could inspect the -result. They targeted open web standards rather than one browser's preferred output. +## Remember ACID? -Gloss brings that **ACID-test philosophy** to generated presentations: +[Acid1](https://www.w3.org/Style/CSS/Test/CSS1/current/sec53.htm), +[Acid2](https://www.webstandards.org/action/acid2/), and +[Acid3](https://acid3.acidtests.org/) gave every browser the same hostile public +artifact. You did not need a white paper to see what the browser understood and +what it did not. -- publish the torture cases instead of hiding them; -- grade the final `.pptx`, not a generation trace or a screenshot; -- test both visible output and standards-based OOXML structure; -- make every assertion, fixture, score, and failure reproducible; -- fail closed when required evidence or rendering stages are missing; -- invite the community to add cases and make the grader stricter. +**Gloss is ACID for presentation decks made by AI.** It publishes the hard +artifact, the exact instructions, the reference renders, and the checks so anyone +can inspect a result, find a failure, and improve the ecosystem. -> **Remember [Acid1](https://www.w3.org/Style/CSS/Test/CSS1/current/sec53.htm) and -> [Acid2](https://www.webstandards.org/action/acid2/)? Gloss is ACID for AI-made -> presentation decks.** +Here, ACID refers to the browser conformance-test tradition—not database +transactions. **Gloss** is the public project. **Gloss v1** is its first bundled +challenge suite. -Here, **ACID refers to the public browser conformance-test tradition, not database -transactions**. +## Try it in three minutes -## Product and suite +1. [Download the native Gloss v1 deck](https://github.com/aronchick/gloss/raw/refs/heads/main/gloss-v1/benchmark/deck/gold/gloss-v1-gold.pptx). +2. Move one object on one slide and a second object on another slide. Save it as + `edited.pptx`. +3. Run the checker. It reports those two native objects—one finding per object. -**Gloss** is the public challenge deck and collaboration project. Its companion -measurement tools make failure modes, evidence, and comparative results easy to -inspect and reproduce. +```bash +git clone https://github.com/aronchick/gloss.git +cd gloss/gloss-v1/grader +uv sync --extra dev --locked +uv run gloss check /path/to/edited.pptx +``` -**Gloss v1 is the first ACID-style PowerPoint conformance suite bundled with -Gloss.** It supplies the current prompt corpus, hostile deck cases, assertions, gold -fixture, mutation tests, and protocol adapter: +An untouched copy returns: -| Name | Role | -| --- | --- | -| **Gloss** | The deliberately difficult presentation deck and public project | -| **ACID tests** | The open conformance-testing philosophy | -| **Gloss v1** | One bundled benchmark protocol and corpus | +```text +Exact match. No native objects changed. +``` -> **Technical-preview status:** the bundled -> [`GLOSS_OPENSPEC.md`](GLOSS_OPENSPEC.md) contract is still Draft and its -> official-release gates remain incomplete. Gloss is live for public collaboration, -> but no result should be treated as an official model leaderboard until a release -> announcement names a frozen scoring cohort. +Move two objects and it returns only two findings: -## Technical preview evidence +```text +2 native objects changed: + Slide 02 · placeholder “Agenda” · changed: position + Slide 12 · placeholder “Document Fields” · changed: position +``` -The current Gloss v1 candidate suite contains 280 schema-valid checks across -rendered pixels and native package structure. Its generated operator suite passes -280/280 positive controls and detects 280/280 generated single-fault mutations. -This proves configured operator behavior only; it is not independent assertion -evidence and it is not a model leaderboard. +`gloss check` is the friendly front door: a strict native-object diff against the +public deck. The deeper `gloss grade` protocol remains available for rendered +comparison, semantic assertions, provenance, and release work. -Every public count is recorded in -[`site/evidence/preview-v1.json`](site/evidence/preview-v1.json) and checked against -the committed mutation reports: +## The deck -```bash -node launch/verify-launch.mjs -``` +Each slide isolates something that can look correct while being built incorrectly. +Open any reference image, then inspect the corresponding +[canonical slide instructions](gloss-v1/benchmark/prompts/variants/canonical). -## Frozen Gloss comparison +### 01 — Cover / Title Stress Test -Gloss includes a frozen comparative bundle produced against Gloss v1: -[`gloss-v1/benchmark/comparative-v1`](gloss-v1/benchmark/comparative-v1). -It contains four repository-owned generation paths, three public seeds per path, -and 12 editable 20-slide decks. The canonical Linux/amd64 grader completed all -240 slide renders. +![Slide 01: Gloss v1 cover](gloss-v1/benchmark/deck/exports/slide-01.png) -The current local artifact scores are 67.68% for the native paths and 62.32% for -the visual paths. These are reproducible workflow baselines, not model rankings. -Every report says `local artifact score; self-reported` and carries no model -attribution. +**Demonstrates:** a cinematic title composition with an image, translucent shapes, +and a master-driven footer. **Tests natively:** layout placeholders, image crop, +z-order, opacity, shadow, and inherited slide numbers. -Reproduce every deck, report, hash, and public bar: +### 02 — Dense Agenda with Layout Semantics -```bash -./gloss-v1/benchmark/comparative-v1/reproduce.sh -``` +![Slide 02: dense agenda](gloss-v1/benchmark/deck/exports/slide-02.png) -Release mode intentionally fails until independent prompt convergence, assertion -provenance and evidence, reviewer approvals, baselines, environment manifests, and -signed release indexes are complete. +**Demonstrates:** a dense agenda that still reads cleanly. **Tests natively:** +placeholder use, real bullet levels, paragraph spacing and indentation, flat +grouping, and exact alignment. -## Score provenance +### 03 — Native Table Stress Test -- **Local grading** measures a deck on the caller's machine. Local results are - self-reported and are not official leaderboard verification. -- **Hosted grading** runs the same protocol adapter inside the controlled Gloss - environment. Every public submission score must carry the exact label - `grading-verified artifact score; generation-attested`. -- Generation strategy, token count, cost, retries, and generation time are supplied - by submitters. They remain **generation-attested** even when the deck itself was - grading-verified. -- A report is not eligible when schema validation did not run or failed, the - canonical renderer or reference exports were unavailable, required slides were - not compared, or any required stage was incomplete. +![Slide 03: performance metrics table](gloss-v1/benchmark/deck/exports/slide-03.png) -## Repository layout +**Demonstrates:** a formatted performance table with a connected annotation. +**Tests natively:** a real table—not a pile of rectangles—plus cell styling, +mixed border weights, paragraph indents, and connector placement. -```text -site/ Static gloss.tools source and public evidence -launch/ Gloss film renderer, verifier, and launch copy -GLOSS_OPENSPEC.md Draft contract for the bundled Gloss v1 suite -gloss-v1/ - benchmark/ ACID-style prompts, cases, checks, fixtures, and evidence - grader/ Gloss v1 protocol adapter and local CLI - schemas/ Bundled ECMA-376 Transitional XSD and report schemas - service/ Pre-release hosted grading components - Dockerfile Canonical Ubuntu 22.04 grading environment -``` +### 04 — Native Chart Stress Test -## Run the bundled suite locally +![Slide 04: revenue chart](gloss-v1/benchmark/deck/exports/slide-04.png) -Gloss currently exercises its first suite through the `gloss` CLI. Requirements: -[uv](https://docs.astral.sh/uv/), Python 3.12, LibreOffice Impress, and `pdftoppm` -from Poppler. +**Demonstrates:** a data-rich chart with annotations and takeaways. **Tests +natively:** an editable chart with correct source data, chart type, labels, +gridlines, legend, overlay shapes, and arrow connectors. -```bash -cd gloss-v1/grader -uv sync --extra dev --locked -uv run gloss validate ../benchmark/deck/gold/gloss-v1-gold.pptx -``` +### 05 — Master Reuse Enforcement -Full grading is fail-closed until a frozen release supplies signed cohort provenance -and the caller supplies a complete artifact-context handoff. During development, -use `validate` for the runnable quarantine/XSD smoke above. After the v1 release -artifacts are published: +![Slide 05: team cards](gloss-v1/benchmark/deck/exports/slide-05.png) -```bash -uv run gloss grade ../submission.pptx \ - --tier 3 \ - --artifact-context ./artifact-context.json \ - --format json -``` +**Demonstrates:** a repeatable card system. **Tests natively:** master and layout +inheritance, grouped card objects, equal distribution, placeholders, and a footer +that is inherited instead of manually copied. -Write a standalone report and private visual diffs: +### 06 — Multilingual Editorial -```bash -uv run gloss grade ../submission.pptx \ - --tier 3 \ - --artifact-context ./artifact-context.json \ - --format html \ - --output report.html \ - --artifacts private-diffs/ -``` +![Slide 06: multilingual editorial layout](gloss-v1/benchmark/deck/exports/slide-06.png) -The protocol CLI exits with status `2` when verification could not be completed. -A normally graded deck may receive a failing score without making the grading -command itself fail. +**Demonstrates:** English, Arabic, and Japanese sharing one editorial canvas. +**Tests natively:** RTL paragraph direction, CJK line breaking, script-specific +fonts, exact Unicode text, and an intentional cross-column overlap. -## Canonical container +### 07 — Image Crop and Mask -```bash -docker build -t gloss-v1 gloss-v1 -docker run --rm \ - --volume "$PWD:/workspace:ro" \ - gloss-v1 grade /workspace/submission.pptx --tier 3 \ - --artifact-context /workspace/artifact-context.json --format json -``` +![Slide 07: image handling](gloss-v1/benchmark/deck/exports/slide-07.png) -## Quality gates +**Demonstrates:** three treatments of approved source imagery. **Tests natively:** +independent crop rectangles, circle and rounded-rectangle masks, asset identity, +and a translucent overlay in the right z-order. -```bash -cd gloss-v1/grader -uv run ruff check . -uv run ruff format --check . -uv run mypy gloss tests -uv run pytest --cov=gloss --cov-fail-under=85 -uv build -uv run pip-audit -``` +### 08 — Overlap, Shadow, and Transparency + +![Slide 08: depth and layering](gloss-v1/benchmark/deck/exports/slide-08.png) + +**Demonstrates:** a cascading stack with convincing depth. **Tests natively:** +precise z-order, five opacity levels, shadows, selective grouping, and a real +gradient fill. + +### 09 — Dense Text Overflow + +![Slide 09: API reference](gloss-v1/benchmark/deck/exports/slide-09.png) + +**Demonstrates:** two code-heavy columns with deliberately different overflow +behavior. **Tests natively:** fixed-size clipping versus auto-shrink, preserved +whitespace, monospaced runs, exact indentation, and line spacing. + +### 10 — Connector and Alignment Diagram + +![Slide 10: system architecture](gloss-v1/benchmark/deck/exports/slide-10.png) + +**Demonstrates:** a legible systems diagram. **Tests natively:** connectors that +remain attached to shapes, elbow routing, arrowheads, grid alignment, labels, and +nested backend groups. + +### 11 — Theme vs. Local Override + +![Slide 11: brand colors](gloss-v1/benchmark/deck/exports/slide-11.png) + +**Demonstrates:** two rows of nearly identical swatches. **Tests natively:** the +invisible semantic difference between theme-linked colors and explicit RGB +overrides—the kind of distinction a screenshot cannot prove. + +### 12 — Native Field Slide + +![Slide 12: document fields](gloss-v1/benchmark/deck/exports/slide-12.png) + +**Demonstrates:** live document metadata next to a static lookalike. **Tests +natively:** slide-number and fixed-date fields, a master footer field, and the +difference between a live field and typed text. + +### 13 — Composite Stress + +![Slide 13: composite stress](gloss-v1/benchmark/deck/exports/slide-13.png) -The bundled validator applies deterministic ECMA-376 Markup Compatibility -preprocessing before validating against the bundled Part 1 Transitional XSD set. -RELAX NG validation is outside the Gloss v1 contract. +**Demonstrates:** chart, table, image, Arabic annotation, and dense callouts in one +composition. **Tests natively:** multiple editable object types and their +relationships surviving together. + +### 14 — RTL-Heavy Comparison + +![Slide 14: RTL systems review](gloss-v1/benchmark/deck/exports/slide-14.png) + +**Demonstrates:** a mirrored Arabic/English comparison. **Tests natively:** +right-to-left paragraphs, mixed bidirectional runs, script-aware fonts, and +mirrored alignment without flattening text. + +### 15 — Rotated Text + +![Slide 15: rotation atlas](gloss-v1/benchmark/deck/exports/slide-15.png) + +**Demonstrates:** typography at 0°, 45°, 90°, 135°, and 270°. **Tests natively:** +real rotation values, anchor points, bounding boxes, and shapes aligned to rotated +text. + +### 16 — Intentional Off-Canvas Bleed + +![Slide 16: beyond the frame](gloss-v1/benchmark/deck/exports/slide-16.png) + +**Demonstrates:** design elements that deliberately continue beyond the canvas. +**Tests natively:** negative coordinates and overhanging geometry without clipping, +normalizing, or “fixing” intentional overflow. + +### 17 — Deep Grouping + +![Slide 17: nested systems](gloss-v1/benchmark/deck/exports/slide-17.png) + +**Demonstrates:** a composition built from nested visual systems. **Tests natively:** +three levels of groups, exact membership, child transforms, and z-order inside each +nesting level. + +### 18 — Multi-Column Editorial + +![Slide 18: three cities editorial](gloss-v1/benchmark/deck/exports/slide-18.png) + +**Demonstrates:** a three-column English/Japanese magazine layout. **Tests +natively:** column structure, repeated image treatments, pull quotes, mixed-script +text, and a patterned background. + +### 19 — Repetition and Consistency + +![Slide 19: design system audit](gloss-v1/benchmark/deck/exports/slide-19.png) + +**Demonstrates:** the deck’s design language as a consistency audit. **Tests +natively:** master-derived typography, colors, spacing, line weights, and reusable +layout behavior across the full document. + +### 20 — Final Torture Slide + +![Slide 20: Gloss synthesis](gloss-v1/benchmark/deck/exports/slide-20.png) + +**Demonstrates:** the whole challenge in one dense synthesis. **Tests natively:** a +chart, table, cropped image, Arabic and Japanese text, rotated type, transparent +layers, connectors, fields, groups, and master inheritance at once. + +## Give the exact challenge to an AI + +The complete prompt is already assembled in one copyable file: + +**[Open the exact Gloss v1 authoring prompt →](gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md)** + +Give the AI that prompt together with: + +- the [20 reference images](gloss-v1/benchmark/deck/exports); +- the [approved assets](gloss-v1/benchmark/assets); +- the [bundled font set](gloss-v1/benchmark/fonts). + +Do not give it the native gold deck. Ask for an editable native presentation, +save the result as `.pptx`, and run `gloss check` against it. The experiment is +intentionally reproducible: every instruction, reference, asset, and check is in +this repository. + +## Native presentations, not one app + +Gloss is about native presentations across PowerPoint, Google Slides, and Keynote. +The principle is format-independent: a chart should remain a chart, text should +remain text, and layout behavior should survive editing. + +Gloss v1 is honestly **PPTX-first**. OOXML is a published standard with an +inspectable package and object structure, which makes independent local checking +possible today. Google Slides and Keynote coverage should use the same public, +artifact-first philosophy as their reliable inspection surfaces mature. Help us +define those adapters in [Issues](https://github.com/aronchick/gloss/issues). + +## Measurement is the support layer + +The simple check answers the first question: *what native objects changed?* The +bundled advanced grader can also inspect rendered pixels, typography, geometry, +charts, tables, layouts, masters, text semantics, relationships, and package +safety. Those tools exist to make this hard presentation easier to understand and +improve; they are not the product’s identity. + +The current candidate suite contains 280 schema-valid checks and generated +operator fixtures. That evidence proves configured checker behavior, not an +official model leaderboard. See [GLOSS_OPENSPEC.md](GLOSS_OPENSPEC.md) for the +draft protocol and [the public evidence bundle](site/evidence/preview-v1.json) for +the receipts. + +## Public today, harder tomorrow + +Gloss v1 is public by design. That also means it may become training data for the +systems it tests. This release is the starting line, not a permanent secret exam. + +Future generations need automated ways to create difficult decks, hold cases back, +delay disclosure, rotate prompts, and publish them after evaluation. The public +suite remains valuable as a shared conformance target; hidden generations will +measure generalization. ## Build it with us -The ACID tradition works because hard cases are public. Help Gloss make generated -decks worth editing: +The browser ACID tests mattered because standards failures became visible and the +community could make the tests better. Gloss should work the same way. -- [Propose a benchmark case](https://github.com/aronchick/gloss/issues/new?template=benchmark-case.yml) -- [Report a grader gap](https://github.com/aronchick/gloss/issues/new?template=grader-bug.yml) +- [Propose a difficult presentation behavior](https://github.com/aronchick/gloss/issues/new?template=benchmark-case.yml) +- [Report a checker gap](https://github.com/aronchick/gloss/issues/new?template=grader-bug.yml) - [Challenge evidence or a public claim](https://github.com/aronchick/gloss/issues/new?template=evidence-challenge.yml) -- Read [CONTRIBUTING.md](CONTRIBUTING.md) before sending a patch +- [Send a pull request](https://github.com/aronchick/gloss/pulls) +- Read [CONTRIBUTING.md](CONTRIBUTING.md) + +## Repository map -## License +```text +gloss-v1/benchmark/deck/ Native challenge deck + 20 reference renders +gloss-v1/benchmark/prompts/ One exact brief + per-slide prompt variants +gloss-v1/benchmark/assets/ Approved source assets +gloss-v1/grader/ Simple checker + advanced grading protocol +site/ Static gloss.tools source +GLOSS_OPENSPEC.md Draft protocol for the bundled v1 suite +``` Code and benchmark materials are released under the -[Apache License 2.0](LICENSE), except third-party or asset files that carry their -own license notices. +[Apache License 2.0](LICENSE), except third-party assets with their own notices. diff --git a/gloss-v1/benchmark/README.md b/gloss-v1/benchmark/README.md index d08d929..c0b811b 100644 --- a/gloss-v1/benchmark/README.md +++ b/gloss-v1/benchmark/README.md @@ -6,6 +6,11 @@ Prompt-derived structural requirements are frozen before gold authoring. Reference exports later become authoritative only for explicitly provenance- linked visual assertions; gold OOXML never creates a scored requirement. +Start with [`prompts/DESIGNER_BRIEF.md`](prompts/DESIGNER_BRIEF.md): it is the +exact, copyable master prompt containing the deck-wide rules and all twenty slide instructions. Provide +it with the reference PNGs in `deck/exports/`, the approved assets, and the bundled +fonts. Do not provide the native gold deck to the system being tested. + ## Integrity validation From `gloss-v1/grader/` using the locked grader environment: diff --git a/gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md b/gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md index ed2df4c..27e4258 100644 --- a/gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md +++ b/gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md @@ -742,27 +742,88 @@ This slide's purpose is to verify that the master/layout is being used correctly ### SLIDE 13: Composite Stress Combines: native chart (pie chart, 6 segments), native table (4×3), one approved image, Arabic text annotation, repeated layout elements, dense callout annotations. +## Explicit v1 constraints + +- Use the **Blank with Footer** layout and title the slide `Composite Stress` in Carlito 36pt Bold at (1.5cm, 0.5cm). +- Add one native pie chart at (1.5cm, 3cm), size 10cm × 8cm. Use six labeled values: `Structure 28`, `Text 22`, `Visual 18`, `Assets 14`, `Fields 10`, `Other 8`; show percentage labels and no legend. +- Add one native 4-row × 3-column table at (13cm, 3cm), size 9cm × 7cm. Header: `Check`, `Status`, `Weight`. Rows: `Schema | Pass | 3`, `Rendering | Pass | 2`, `Assets | Review | 1`. +- Insert `cityscape.png` at (23cm, 3cm), size 8cm × 6cm, cropping 15% from the left. Use only asset ID `cityscape` from the manifest. +- Add the Arabic annotation `الهيكل والمحتوى والعرض في اختبار واحد` in Noto Sans Arabic 14pt with native RTL direction. +- Add three dense rounded callouts labeled `NATIVE CHART`, `NATIVE TABLE`, and `APPROVED ASSET`; connect each to its target and keep all data labels visible. + ### SLIDE 14: RTL-Heavy Comparison Two-column layout with heavy Arabic text on left (5+ paragraphs with mixed Arabic/English), English summary on right. Bidirectional text within single paragraphs (e.g., "The الذكاء الاصطناعي revolution"). Mirrored alignment: Arabic column is right-aligned, English column is left-aligned. +## Explicit v1 constraints + +- Use the **Two-Column** layout with title `RTL Systems Review`. +- The left column contains at least five separate Arabic paragraphs in Noto Sans Arabic 14pt with native RTL paragraph direction. Include these exact phrases: `مراجعة الأنظمة الموزعة`, `دقة العرض`, `سلامة البنية`, `The الذكاء الاصطناعي revolution`, and `الإصدار Gloss v1`. +- The right column contains five English summary paragraphs in Liberation Sans 14pt, beginning with `A structural benchmark must preserve meaning and direction.` +- Right-align the Arabic column and left-align the English column. Mirror their inner padding and keep a 1pt teal divider centered between them. +- Mixed Arabic/English runs must preserve Unicode character order and visible punctuation without converting text to outlines. + ### SLIDE 15: Rotated Text Multiple text boxes at different rotation angles: 0°, 45°, 90°, 135°, 270°. Supporting shapes aligned to the rotated text. Tests exact anchor points for rotated elements. +## Explicit v1 constraints + +- Use the **Blank with Footer** layout with title `Rotation Atlas`. +- Create five text boxes labeled exactly `Baseline 0°`, `Diagonal 45°`, `Vertical 90°`, `Reverse 135°`, and `Vertical 270°`. +- Apply rotations of 0°, 45°, 90°, 135°, and 270° respectively. Each text box is 5cm × 1.5cm, uses Liberation Sans 16pt Bold, and has a matching 6cm × 2cm supporting rectangle sharing its center and rotation. +- Distribute the five pairs evenly from x=2cm through x=28cm on a common y=8cm anchor line. Keep text editable and store rotations in native OOXML transforms. + ### SLIDE 16: Intentional Off-Canvas Bleed Objects deliberately extending beyond the slide canvas edges — a large circle at (-3cm, -2cm) partly visible, a rectangle extending 5cm past the right edge. These are intentional design choices, not errors. +## Explicit v1 constraints + +- Use the **Blank with Footer** layout with title `Beyond the Frame`. +- Place a 10cm diameter coral circle at (-3cm, -2cm), a 12cm × 6cm teal rectangle at (28.867cm, 6cm) so it extends 5cm beyond the right edge, and a 16cm × 3cm gold strip at (8cm, -1cm). +- Add on-canvas labels `INTENTIONAL BLEED` and `Negative coordinates are part of the composition.` +- Preserve negative and over-bound coordinates in OOXML. Do not replace the composition with a screenshot, hide content under opaque shapes, or move the objects fully on-canvas. + ### SLIDE 17: Deep Grouping 3 levels of nested groups. Inner groups contain exactly 3 shapes each. Middle groups contain exactly 2 inner groups. Outer group wraps everything. Z-order must be exact within each nesting level. +## Explicit v1 constraints + +- Use the **Blank with Footer** layout with title `Nested Systems`. +- Build one outer group containing three middle groups. Each middle group contains two inner groups; each inner group contains three leaf shapes (18 leaf shapes total). +- Use native group objects at all three levels. Label the middle groups `INPUT`, `PROCESS`, and `OUTPUT`; label inner groups `A1`, `A2`, `B1`, `B2`, `C1`, and `C2`. +- Within every inner group, z-order is circle below rounded rectangle below label. The full outer group's bounding box has top-left position (3cm, 3cm) and size 27cm × 13cm. + ### SLIDE 18: Multi-Column Editorial 3-column magazine-style layout with mixed English/Japanese text. Each column has a header image (from assets), body text, and a pull quote. Pattern fill on the background of one column. +## Explicit v1 constraints + +- Use the **Blank with Footer** layout with title `Three Cities / 三つの都市`. +- Build three equal 9.5cm-wide editorial columns with 0.9cm gutters. Each column contains a 9.5cm × 3.5cm approved header image, a heading, body copy, and a pull quote. +- Column images, left to right: `cityscape.png`, `texture-pattern.png`, `cityscape.png`. Center the crop of each image and do not use unlisted media. +- Headings: `Systems`, `システム`, `Shared Futures`. Include the exact Japanese line `生成されたスライドは構造と意味を保持します。` +- Apply the approved `texture-pattern.png` as a tiled background only to the center column at 20% opacity. Use 0.9cm gutters consistently. + ### SLIDE 19: Repetition and Consistency Visually identical to the design system — every element must match the master's font sizes, colors, spacing, and line weights exactly. Tests deck-wide consistency. Internal hyperlinks to Slides 1 and 5 (not external URLs). +## Explicit v1 constraints + +- Use the **Content Slide** layout with title `Design System Audit`. +- Show five samples labeled `Typography`, `Palette`, `Spacing`, `Line Weight`, and `Navigation`. Each sample must use the deck-level tokens exactly: Carlito 36pt, Liberation Sans 18pt, navy `#1B2A4A`, coral `#E8634A`, teal `#2AACB8`, and a 0.5pt teal rule. +- Create two native internal hyperlinks: `Return to Cover` targets Slide 1 and `Meet the Team` targets Slide 5. Do not use external URLs. +- Footer line, company name, accent bar, and slide number remain inherited from the master/layout; do not duplicate them as slide-local shapes. + ### SLIDE 20: Final Torture Slide Everything combined: chart (line chart), table (3×6), approved image (cropped), Arabic + Japanese text, rotated text box at 45°, overlapping semi-transparent shapes, nested group, master-inherited footer, gradient fill, bullet list, slide number field — all on one slide. +## Explicit v1 constraints + +- Use the **Blank with Footer** layout with title `Gloss Synthesis`. +- Add one native line chart with series `Structural`, `Visual`, and `Combined` across categories `L1`, `L2`, `L3`; values are `72, 84, 96`, `68, 82, 95`, and `70, 83, 97`. +- Add one native 3-row × 6-column table. Header: `Tier`, `Slides`, `Checks`, `SSIM`, `Schema`, `Status`; rows: `L1 | 5 | 70 | 0.9999 | Pass | Ready` and `L3 | 20 | 280 | 0.9999 | Pass | Ready`. +- Insert `hero-abstract.png` with a 20% left crop. Add Arabic `اختبار شامل` with native RTL direction and Japanese `総合テスト` in Noto Sans CJK JP. +- Include a 45° text box labeled `ROTATED`, three overlapping semi-transparent shapes inside a two-level group, a navy-to-teal gradient, a three-item bullet list, a live slide-number field, and the inherited master footer. + --- ## Asset Manifest diff --git a/gloss-v1/benchmark/prompts/validation/slide-20.md b/gloss-v1/benchmark/prompts/validation/slide-20.md index bfc932d..0fc4665 100644 --- a/gloss-v1/benchmark/prompts/validation/slide-20.md +++ b/gloss-v1/benchmark/prompts/validation/slide-20.md @@ -3,9 +3,9 @@ "authors": [], "pairwise_similarity_diagnostics": [], "prompt_hashes": { - "canonical": "sha256:69711e77502102776aae817c7417faad5d3ae0ea8e29c823554cbc27c5b989d9", - "paraphrase-a": "sha256:86adb541d0961cefeb3d1388cdc4d6fea1f989d1485bb6dafbc5043d177204a7", - "paraphrase-b": "sha256:943121b317a29b1be5cf83776833d3c0c8e25aef6176f3d1bdefd3e8d4e72e05" + "canonical": "sha256:ba8e51e1becddfd40e2f2a02c5161a1caa09481255fcb9ff586f6b32ff866245", + "paraphrase-a": "sha256:c00f3398a053de3f9ac2b9a34697a8cb4eebefaf90ee1e73bc878ba24cd58081", + "paraphrase-b": "sha256:e37739606bb0dd690de9ac19cd76c51d2a95f3310d46c2c4e1c4b18d00474ac4" }, "record_id": "gloss-prompt-validation-slide-20", "round_id": null, @@ -18,9 +18,9 @@ - Prompt contract status: complete - Canonical/paraphrase hard-constraint parity: pass -- Canonical SHA-256: `69711e77502102776aae817c7417faad5d3ae0ea8e29c823554cbc27c5b989d9` -- Paraphrase A SHA-256: `86adb541d0961cefeb3d1388cdc4d6fea1f989d1485bb6dafbc5043d177204a7` -- Paraphrase B SHA-256: `943121b317a29b1be5cf83776833d3c0c8e25aef6176f3d1bdefd3e8d4e72e05` +- Canonical SHA-256: `ba8e51e1becddfd40e2f2a02c5161a1caa09481255fcb9ff586f6b32ff866245` +- Paraphrase A SHA-256: `c00f3398a053de3f9ac2b9a34697a8cb4eebefaf90ee1e73bc878ba24cd58081` +- Paraphrase B SHA-256: `e37739606bb0dd690de9ac19cd76c51d2a95f3310d46c2c4e1c4b18d00474ac4` - Independent-author convergence: not run - Required blinded author count: 3 - Mandatory assertion pass status: pending for all 3 implementations diff --git a/gloss-v1/benchmark/prompts/variants/canonical/slide-20.md b/gloss-v1/benchmark/prompts/variants/canonical/slide-20.md index 7cace1a..959aa69 100644 --- a/gloss-v1/benchmark/prompts/variants/canonical/slide-20.md +++ b/gloss-v1/benchmark/prompts/variants/canonical/slide-20.md @@ -4,8 +4,6 @@ The natural-language requirements below are the primary directive. Use the refer Everything combined: chart (line chart), table (3×6), approved image (cropped), Arabic + Japanese text, rotated text box at 45°, overlapping semi-transparent shapes, nested group, master-inherited footer, gradient fill, bullet list, slide number field — all on one slide. ---- - ## Explicit v1 constraints - Use the **Blank with Footer** layout with title `Gloss Synthesis`. @@ -13,3 +11,5 @@ Everything combined: chart (line chart), table (3×6), approved image (cropped), - Add one native 3-row × 6-column table. Header: `Tier`, `Slides`, `Checks`, `SSIM`, `Schema`, `Status`; rows: `L1 | 5 | 70 | 0.9999 | Pass | Ready` and `L3 | 20 | 280 | 0.9999 | Pass | Ready`. - Insert `hero-abstract.png` with a 20% left crop. Add Arabic `اختبار شامل` with native RTL direction and Japanese `総合テスト` in Noto Sans CJK JP. - Include a 45° text box labeled `ROTATED`, three overlapping semi-transparent shapes inside a two-level group, a navy-to-teal gradient, a three-item bullet list, a live slide-number field, and the inherited master footer. + +--- diff --git a/gloss-v1/benchmark/prompts/variants/paraphrase-a/slide-20.md b/gloss-v1/benchmark/prompts/variants/paraphrase-a/slide-20.md index 930409f..7046460 100644 --- a/gloss-v1/benchmark/prompts/variants/paraphrase-a/slide-20.md +++ b/gloss-v1/benchmark/prompts/variants/paraphrase-a/slide-20.md @@ -4,8 +4,6 @@ The natural-language requirements that follow are the primary directive. Treat t Everything combined: chart (line chart), table (3×6), approved image (cropped), Arabic + Japanese text, rotated text box at 45°, overlapping semi-transparent shapes, nested group, master-inherited footer, gradient fill, bullet list, slide number field — all on one slide. ---- - ## Mandatory v1 requirements - Select the **Blank with Footer** layout with title `Gloss Synthesis`. @@ -13,3 +11,5 @@ Everything combined: chart (line chart), table (3×6), approved image (cropped), - Include one native 3-row × 6-column table. Header: `Tier`, `Slides`, `Checks`, `SSIM`, `Schema`, `Status`; rows: `L1 | 5 | 70 | 0.9999 | Pass | Ready` and `L3 | 20 | 280 | 0.9999 | Pass | Ready`. - Position `hero-abstract.png` with a 20% left crop. Include Arabic `اختبار شامل` with native RTL direction and Japanese `総合テスト` in Noto Sans CJK JP. - Include a 45° text box labeled `ROTATED`, three overlapping semi-transparent shapes inside a two-level group, a navy-to-teal gradient, a three-item bullet list, a live slide-number field, and the inherited master footer. + +--- diff --git a/gloss-v1/benchmark/prompts/variants/paraphrase-b/slide-20.md b/gloss-v1/benchmark/prompts/variants/paraphrase-b/slide-20.md index 36b29fe..69d9479 100644 --- a/gloss-v1/benchmark/prompts/variants/paraphrase-b/slide-20.md +++ b/gloss-v1/benchmark/prompts/variants/paraphrase-b/slide-20.md @@ -4,8 +4,6 @@ The primary directive is the following set of natural-language requirements. Con Everything combined: chart (line chart), table (3×6), approved image (cropped), Arabic + Japanese text, rotated text box at 45°, overlapping semi-transparent shapes, nested group, master-inherited footer, gradient fill, bullet list, slide number field — all on one slide. ---- - ## Fixed v1 acceptance constraints - Adopt the **Blank with Footer** layout with title `Gloss Synthesis`. @@ -13,3 +11,5 @@ Everything combined: chart (line chart), table (3×6), approved image (cropped), - Provide one native 3-row × 6-column table. Header: `Tier`, `Slides`, `Checks`, `SSIM`, `Schema`, `Status`; rows: `L1 | 5 | 70 | 0.9999 | Pass | Ready` and `L3 | 20 | 280 | 0.9999 | Pass | Ready`. - Embed `hero-abstract.png` with a 20% left crop. Provide Arabic `اختبار شامل` with native RTL direction and Japanese `総合テスト` in Noto Sans CJK JP. - Include a 45° text box labeled `ROTATED`, three overlapping semi-transparent shapes inside a two-level group, a navy-to-teal gradient, a three-item bullet list, a live slide-number field, and the inherited master footer. + +--- diff --git a/gloss-v1/benchmark/requirements/prompt-requirements.json b/gloss-v1/benchmark/requirements/prompt-requirements.json index 3f48a40..51e2092 100644 --- a/gloss-v1/benchmark/requirements/prompt-requirements.json +++ b/gloss-v1/benchmark/requirements/prompt-requirements.json @@ -149,7 +149,7 @@ "source_type": "prompt", "authority": "primary", "path": "benchmark/prompts/variants/canonical/slide-20.md", - "sha256": "69711e77502102776aae817c7417faad5d3ae0ea8e29c823554cbc27c5b989d9" + "sha256": "ba8e51e1becddfd40e2f2a02c5161a1caa09481255fcb9ff586f6b32ff866245" } ], "independent_reviews": [], @@ -15135,8 +15135,8 @@ "provenance": { "source_id": "slide-20-prompt", "source_type": "prompt", - "line_start": 11, - "line_end": 11, + "line_start": 9, + "line_end": 9, "excerpt": "- Use the **Blank with Footer** layout with title `Gloss Synthesis`.", "excerpt_sha256": "879c1e2c7eb467566cfda47f1b0c44f6f1e75e73e865c059a25c437ddbe2a857" }, @@ -15175,8 +15175,8 @@ "provenance": { "source_id": "slide-20-prompt", "source_type": "prompt", - "line_start": 12, - "line_end": 12, + "line_start": 10, + "line_end": 10, "excerpt": "- Add one native line chart with series `Structural`, `Visual`, and `Combined` across categories `L1`, `L2`, `L3`; values are `72, 84, 96`, `68, 82, 95`, and `70, 83, 97`.", "excerpt_sha256": "c9290c0038e5213fd6ae2aec7690f705665911923945219b13544c29ce36c99b" }, @@ -15215,8 +15215,8 @@ "provenance": { "source_id": "slide-20-prompt", "source_type": "prompt", - "line_start": 13, - "line_end": 13, + "line_start": 11, + "line_end": 11, "excerpt": "- Add one native 3-row × 6-column table. Header: `Tier`, `Slides`, `Checks`, `SSIM`, `Schema`, `Status`; rows: `L1 | 5 | 70 | 0.9999 | Pass | Ready` and `L3 | 20 | 280 | 0.9999 | Pass | Ready`.", "excerpt_sha256": "002dfc07ea3b536a91fb863588727de959fe95f3c9020ca0e766d2b0df28c028" }, @@ -15255,8 +15255,8 @@ "provenance": { "source_id": "slide-20-prompt", "source_type": "prompt", - "line_start": 14, - "line_end": 14, + "line_start": 12, + "line_end": 12, "excerpt": "- Insert `hero-abstract.png` with a 20% left crop. Add Arabic `اختبار شامل` with native RTL direction and Japanese `総合テスト` in Noto Sans CJK JP.", "excerpt_sha256": "393f213352644b945cbf5a9bd8587806259012cc36eca0c162e1322a26579589" }, @@ -15295,8 +15295,8 @@ "provenance": { "source_id": "slide-20-prompt", "source_type": "prompt", - "line_start": 15, - "line_end": 15, + "line_start": 13, + "line_end": 13, "excerpt": "- Include a 45° text box labeled `ROTATED`, three overlapping semi-transparent shapes inside a two-level group, a navy-to-teal gradient, a three-item bullet list, a live slide-number field, and the inherited master footer.", "excerpt_sha256": "641b7b5911d4964ef11e26e7ce454de8d7e7a0ca3cfd51785b79aca194f6aa64" }, diff --git a/gloss-v1/benchmark/tools/build_corpus.py b/gloss-v1/benchmark/tools/build_corpus.py index b039bc8..95d6f5f 100644 --- a/gloss-v1/benchmark/tools/build_corpus.py +++ b/gloss-v1/benchmark/tools/build_corpus.py @@ -368,79 +368,6 @@ class SlideSpec: } -LEVEL3_DETAILS = { - 13: """ -## Explicit v1 constraints - -- Use the **Blank with Footer** layout and title the slide `Composite Stress` in Carlito 36pt Bold at (1.5cm, 0.5cm). -- Add one native pie chart at (1.5cm, 3cm), size 10cm × 8cm. Use six labeled values: `Structure 28`, `Text 22`, `Visual 18`, `Assets 14`, `Fields 10`, `Other 8`; show percentage labels and no legend. -- Add one native 4-row × 3-column table at (13cm, 3cm), size 9cm × 7cm. Header: `Check`, `Status`, `Weight`. Rows: `Schema | Pass | 3`, `Rendering | Pass | 2`, `Assets | Review | 1`. -- Insert `cityscape.png` at (23cm, 3cm), size 8cm × 6cm, cropping 15% from the left. Use only asset ID `cityscape` from the manifest. -- Add the Arabic annotation `الهيكل والمحتوى والعرض في اختبار واحد` in Noto Sans Arabic 14pt with native RTL direction. -- Add three dense rounded callouts labeled `NATIVE CHART`, `NATIVE TABLE`, and `APPROVED ASSET`; connect each to its target and keep all data labels visible. -""", - 14: """ -## Explicit v1 constraints - -- Use the **Two-Column** layout with title `RTL Systems Review`. -- The left column contains at least five separate Arabic paragraphs in Noto Sans Arabic 14pt with native RTL paragraph direction. Include these exact phrases: `مراجعة الأنظمة الموزعة`, `دقة العرض`, `سلامة البنية`, `The الذكاء الاصطناعي revolution`, and `الإصدار Gloss v1`. -- The right column contains five English summary paragraphs in Liberation Sans 14pt, beginning with `A structural benchmark must preserve meaning and direction.` -- Right-align the Arabic column and left-align the English column. Mirror their inner padding and keep a 1pt teal divider centered between them. -- Mixed Arabic/English runs must preserve Unicode character order and visible punctuation without converting text to outlines. -""", - 15: """ -## Explicit v1 constraints - -- Use the **Blank with Footer** layout with title `Rotation Atlas`. -- Create five text boxes labeled exactly `Baseline 0°`, `Diagonal 45°`, `Vertical 90°`, `Reverse 135°`, and `Vertical 270°`. -- Apply rotations of 0°, 45°, 90°, 135°, and 270° respectively. Each text box is 5cm × 1.5cm, uses Liberation Sans 16pt Bold, and has a matching 6cm × 2cm supporting rectangle sharing its center and rotation. -- Distribute the five pairs evenly from x=2cm through x=28cm on a common y=8cm anchor line. Keep text editable and store rotations in native OOXML transforms. -""", - 16: """ -## Explicit v1 constraints - -- Use the **Blank with Footer** layout with title `Beyond the Frame`. -- Place a 10cm diameter coral circle at (-3cm, -2cm), a 12cm × 6cm teal rectangle at (28.867cm, 6cm) so it extends 5cm beyond the right edge, and a 16cm × 3cm gold strip at (8cm, -1cm). -- Add on-canvas labels `INTENTIONAL BLEED` and `Negative coordinates are part of the composition.` -- Preserve negative and over-bound coordinates in OOXML. Do not replace the composition with a screenshot, hide content under opaque shapes, or move the objects fully on-canvas. -""", - 17: """ -## Explicit v1 constraints - -- Use the **Blank with Footer** layout with title `Nested Systems`. -- Build one outer group containing three middle groups. Each middle group contains two inner groups; each inner group contains three leaf shapes (18 leaf shapes total). -- Use native group objects at all three levels. Label the middle groups `INPUT`, `PROCESS`, and `OUTPUT`; label inner groups `A1`, `A2`, `B1`, `B2`, `C1`, and `C2`. -- Within every inner group, z-order is circle below rounded rectangle below label. The full outer group's bounding box has top-left position (3cm, 3cm) and size 27cm × 13cm. -""", - 18: """ -## Explicit v1 constraints - -- Use the **Blank with Footer** layout with title `Three Cities / 三つの都市`. -- Build three equal 9.5cm-wide editorial columns with 0.9cm gutters. Each column contains a 9.5cm × 3.5cm approved header image, a heading, body copy, and a pull quote. -- Column images, left to right: `cityscape.png`, `texture-pattern.png`, `cityscape.png`. Center the crop of each image and do not use unlisted media. -- Headings: `Systems`, `システム`, `Shared Futures`. Include the exact Japanese line `生成されたスライドは構造と意味を保持します。` -- Apply the approved `texture-pattern.png` as a tiled background only to the center column at 20% opacity. Use 0.9cm gutters consistently. -""", - 19: """ -## Explicit v1 constraints - -- Use the **Content Slide** layout with title `Design System Audit`. -- Show five samples labeled `Typography`, `Palette`, `Spacing`, `Line Weight`, and `Navigation`. Each sample must use the deck-level tokens exactly: Carlito 36pt, Liberation Sans 18pt, navy `#1B2A4A`, coral `#E8634A`, teal `#2AACB8`, and a 0.5pt teal rule. -- Create two native internal hyperlinks: `Return to Cover` targets Slide 1 and `Meet the Team` targets Slide 5. Do not use external URLs. -- Footer line, company name, accent bar, and slide number remain inherited from the master/layout; do not duplicate them as slide-local shapes. -""", - 20: """ -## Explicit v1 constraints - -- Use the **Blank with Footer** layout with title `Gloss Synthesis`. -- Add one native line chart with series `Structural`, `Visual`, and `Combined` across categories `L1`, `L2`, `L3`; values are `72, 84, 96`, `68, 82, 95`, and `70, 83, 97`. -- Add one native 3-row × 6-column table. Header: `Tier`, `Slides`, `Checks`, `SSIM`, `Schema`, `Status`; rows: `L1 | 5 | 70 | 0.9999 | Pass | Ready` and `L3 | 20 | 280 | 0.9999 | Pass | Ready`. -- Insert `hero-abstract.png` with a 20% left crop. Add Arabic `اختبار شامل` with native RTL direction and Japanese `総合テスト` in Noto Sans CJK JP. -- Include a 45° text box labeled `ROTATED`, three overlapping semi-transparent shapes inside a two-level group, a navy-to-teal gradient, a three-item bullet list, a live slide-number field, and the inherited master footer. -""", -} - - PARAPHRASE_A = { "What to build": "Required construction", "must contain": "is required to include", @@ -555,8 +482,6 @@ def canonical_sections() -> dict[int, str]: "Use the reference image only as supplementary visual guidance.\n\n" ) content = heading + intro + body - if number in LEVEL3_DETAILS: - content += "\n\n" + LEVEL3_DETAILS[number].strip() + "\n" sections[number] = content.strip() + "\n" if set(sections) != set(range(1, 21)): raise RuntimeError(f"expected slide sections 1-20, found {sorted(sections)}") diff --git a/gloss-v1/grader/README.md b/gloss-v1/grader/README.md index cd747b3..7b4d1fc 100644 --- a/gloss-v1/grader/README.md +++ b/gloss-v1/grader/README.md @@ -1,11 +1,27 @@ -# Gloss Grader +# Gloss Checker -The Gloss CLI performs quarantine checks, deterministic ECMA-376 Markup Compatibility preprocessing, Part 1 Transitional XSD validation, LibreOffice export, SSIM comparison, native object inspection, checklist evaluation, and report generation. +The friendly front door is a native-object check against the public Gloss deck: + +```bash +uv sync --extra dev --locked +uv run gloss check ../benchmark/deck/gold/gloss-v1-gold.pptx +uv run gloss check /path/to/edited.pptx +``` + +An untouched deck returns zero findings. Change the position, size, text, type, +rotation, field, chart, table, image, fill, shadow, group, or z-order of one native +object and `gloss check` returns one aggregated finding for that object. Use +`--format json` for machine-readable output or `--reference other.pptx` to compare +against another deck. + +The same CLI also contains the deeper Gloss v1 grading protocol: quarantine checks, +deterministic ECMA-376 Markup Compatibility preprocessing, Part 1 Transitional XSD +validation, LibreOffice export, SSIM comparison, native inspection, checklist +evaluation, and release-grade reports. The wheel contains the benchmark corpus and schemas. Source and Docker layouts are also discovered automatically; `--benchmark-dir` or `GLOSS_BENCHMARK_DIR` can select an explicit corpus. ```bash -uv sync --extra dev --locked uv run gloss validate ../benchmark/deck/gold/gloss-v1-gold.pptx uv run gloss grade ../submission.pptx --tier 3 \ --artifact-context ./local-artifact-context.json --format json @@ -13,7 +29,9 @@ uv run gloss grade ../submission.pptx --tier 3 \ --artifact-context ./local-artifact-context.json --format html -o report.html ``` -Output formats are `text`, `json`, and standalone `html`. Use `--artifacts DIR` to retain `diff-slide-NN.png` visual diagnostics. Incomplete verification exits with status `2` and is always reported as `eligible: false`. +Advanced `grade` output formats are `text`, `json`, and standalone `html`. Use +`--artifacts DIR` to retain `diff-slide-NN.png` visual diagnostics. Incomplete +verification exits with status `2` and is always reported as `eligible: false`. `--artifact-context` is required and must name a complete JSON serialization of `ArtifactReportContext`, including explicit `null` values. Missing or unknown fields are rejected. The diff --git a/gloss-v1/grader/gloss/cli.py b/gloss-v1/grader/gloss/cli.py index dc5f4a8..56bb1a4 100644 --- a/gloss-v1/grader/gloss/cli.py +++ b/gloss-v1/grader/gloss/cli.py @@ -1,4 +1,4 @@ -"""Gloss CLI — benchmark grading tool.""" +"""Gloss CLI — native presentation challenge and checker.""" import json from pathlib import Path @@ -15,7 +15,81 @@ @click.group() @click.version_option(version=__version__) def main() -> None: - """Gloss benchmark grader — ECMA-376 slide generation fidelity testing.""" + """Gloss — the open challenge for native AI-made presentations.""" + + +@main.command() +@click.argument( + "submission", + type=click.Path(exists=True, dir_okay=False, readable=True, path_type=Path), +) +@click.option( + "--reference", + type=click.Path(exists=True, dir_okay=False, readable=True, path_type=Path), + default=None, + help="Reference .pptx; defaults to the bundled Gloss v1 deck", +) +@click.option( + "--format", + "fmt", + type=click.Choice(["text", "json"]), + default="text", + show_default=True, +) +def check(submission: Path, reference: Path | None, fmt: str) -> None: + """Report native objects changed from the public Gloss deck.""" + import zipfile + + from lxml import etree + + from gloss.compare import compare_native_decks + from gloss.mce import MCEProfileError + from gloss.resources import BenchmarkDataError, resolve_benchmark_dir + + try: + resolved_reference = reference + if resolved_reference is None: + resolved_reference = resolve_benchmark_dir() / "deck" / "gold" / "gloss-v1-gold.pptx" + if not resolved_reference.is_file(): + raise FileNotFoundError(f"Reference deck is missing: {resolved_reference}") + changes = compare_native_decks(submission, resolved_reference) + except ( + BenchmarkDataError, + FileNotFoundError, + MCEProfileError, + OSError, + ValueError, + zipfile.BadZipFile, + etree.XMLSyntaxError, + ) as exc: + console.print(f"[red]Check could not run:[/red] {exc}") + raise click.exceptions.Exit(2) from exc + + if fmt == "json": + click.echo( + json.dumps( + { + "status": "exact" if not changes else "changed", + "changed_objects": len(changes), + "changes": [change.as_dict() for change in changes], + }, + indent=2, + ) + ) + elif not changes: + console.print("[green]Exact match.[/green] No native objects changed.") + else: + noun = "object" if len(changes) == 1 else "objects" + console.print(f"[yellow]{len(changes)} native {noun} changed:[/yellow]") + for change in changes: + location = "Deck" if change.slide_number == 0 else f"Slide {change.slide_number:02d}" + fields = ", ".join(change.changed_fields) + console.print( + f" [bold]{location}[/bold] · {change.label} · {change.change_type}: {fields}" + ) + + if changes: + raise click.exceptions.Exit(1) @main.command("build-environment-candidate") diff --git a/gloss-v1/grader/gloss/compare.py b/gloss-v1/grader/gloss/compare.py index d878154..ad204dc 100644 --- a/gloss-v1/grader/gloss/compare.py +++ b/gloss-v1/grader/gloss/compare.py @@ -1,8 +1,9 @@ -"""Stage 2: Visual comparison — SSIM-based slide comparison.""" +"""Visual and native-object comparison for presentation artifacts.""" from __future__ import annotations -from typing import TYPE_CHECKING, cast +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, cast import numpy as np from PIL import Image @@ -13,9 +14,223 @@ if TYPE_CHECKING: from pathlib import Path + from gloss.inspect_ooxml import DeckGraph, SceneObject + SSIM_THRESHOLD = 0.9999 +@dataclass(frozen=True) +class NativeObjectChange: + """One changed native presentation object, aggregated into one finding.""" + + slide_number: int + object_path: str + label: str + changed_fields: tuple[str, ...] + change_type: str = "changed" + + def as_dict(self) -> dict[str, Any]: + return { + "slide": self.slide_number, + "object_path": self.object_path, + "label": self.label, + "change_type": self.change_type, + "changed_fields": list(self.changed_fields), + } + + +def compare_native_decks(submission: Path, reference: Path) -> list[NativeObjectChange]: + """Return one finding for each native object that differs from ``reference``. + + This is the intentionally small front-door check used after editing the public + Gloss deck. It compares the native object tree rather than rendered pixels and + collapses any number of property differences on one object into one finding. + The release grader remains responsible for full visual and protocol scoring. + """ + from gloss.inspect_ooxml import extract_deck_graph + + submission_graph = extract_deck_graph(submission) + reference_graph = extract_deck_graph(reference) + return _compare_deck_graphs(submission_graph, reference_graph) + + +def _compare_deck_graphs( + submission: DeckGraph, + reference: DeckGraph, +) -> list[NativeObjectChange]: + changes: list[NativeObjectChange] = [] + if len(submission.slides) != len(reference.slides): + changes.append( + NativeObjectChange( + slide_number=0, + object_path="deck", + label="slide count", + changed_fields=("slide_count",), + ) + ) + + shared_slides = min(len(submission.slides), len(reference.slides)) + for index in range(shared_slides): + actual_slide = submission.slides[index] + expected_slide = reference.slides[index] + slide_number = expected_slide.slide_number + slide_fields = tuple( + field + for field, actual, expected in ( + ("layout", actual_slide.layout_ref, expected_slide.layout_ref), + ("layout_name", actual_slide.layout_name, expected_slide.layout_name), + ("master", actual_slide.master_name, expected_slide.master_name), + ) + if actual != expected + ) + if slide_fields: + changes.append( + NativeObjectChange( + slide_number=slide_number, + object_path="slide", + label="slide properties", + changed_fields=slide_fields, + ) + ) + + actual_objects = dict(_flatten_objects(actual_slide.objects)) + expected_objects = dict(_flatten_objects(expected_slide.objects)) + for path in sorted(actual_objects.keys() | expected_objects.keys()): + actual = actual_objects.get(path) + expected = expected_objects.get(path) + object_path = _format_object_path(path) + if actual is None and expected is not None: + changes.append( + NativeObjectChange( + slide_number=slide_number, + object_path=object_path, + label=_object_label(expected, object_path), + changed_fields=("presence",), + change_type="missing", + ) + ) + continue + if expected is None and actual is not None: + changes.append( + NativeObjectChange( + slide_number=slide_number, + object_path=object_path, + label=_object_label(actual, object_path), + changed_fields=("presence",), + change_type="unexpected", + ) + ) + continue + if actual is None or expected is None: + continue + fields = _changed_object_fields(actual, expected) + if fields: + changes.append( + NativeObjectChange( + slide_number=slide_number, + object_path=object_path, + label=_object_label(expected, object_path), + changed_fields=fields, + ) + ) + + for slide in reference.slides[shared_slides:]: + changes.append( + NativeObjectChange( + slide_number=slide.slide_number, + object_path="slide", + label="entire slide", + changed_fields=("presence",), + change_type="missing", + ) + ) + for slide in submission.slides[shared_slides:]: + changes.append( + NativeObjectChange( + slide_number=slide.slide_number, + object_path="slide", + label="entire slide", + changed_fields=("presence",), + change_type="unexpected", + ) + ) + return changes + + +def _flatten_objects( + objects: list[SceneObject], + parent: tuple[int, ...] = (), +) -> list[tuple[tuple[int, ...], SceneObject]]: + flattened: list[tuple[tuple[int, ...], SceneObject]] = [] + for index, obj in enumerate(objects, 1): + path = parent + (index,) + flattened.append((path, obj)) + flattened.extend(_flatten_objects(obj.children, path)) + return flattened + + +def _format_object_path(path: tuple[int, ...]) -> str: + return ".".join(str(value) for value in path) + + +def _object_label(obj: SceneObject, object_path: str) -> str: + object_type = "placeholder" if obj.placeholder_type else obj.obj_type + text = " ".join(run.text.strip() for run in obj.text_runs if run.text.strip()) + if text: + excerpt = text if len(text) <= 42 else f"{text[:39].rstrip()}…" + return f"{object_type} “{excerpt}”" + if obj.name: + return f"{object_type} “{obj.name}”" + return f"{object_type} {object_path}" + + +def _changed_object_fields(actual: SceneObject, expected: SceneObject) -> tuple[str, ...]: + fields: list[str] = [] + if actual.bbox[:2] != expected.bbox[:2]: + fields.append("position") + if actual.bbox[2:] != expected.bbox[2:]: + fields.append("size") + comparisons = ( + ("type", actual.obj_type, expected.obj_type), + ("name", actual.name, expected.name), + ("z_order", actual.z_index, expected.z_index), + ("rotation", actual.rotation, expected.rotation), + ("text", _text_snapshot(actual), _text_snapshot(expected)), + ( + "placeholder", + (actual.placeholder_type, actual.placeholder_idx), + (expected.placeholder_type, expected.placeholder_idx), + ), + ( + "table", + (actual.is_table, actual.table_rows, actual.table_cols), + (expected.is_table, expected.table_rows, expected.table_cols), + ), + ("chart", (actual.is_chart, actual.chart_type), (expected.is_chart, expected.chart_type)), + ("field", actual.field_type, expected.field_type), + ("fill", (actual.fill_type, actual.opacity), (expected.fill_type, expected.opacity)), + ("shadow", actual.has_shadow, expected.has_shadow), + ( + "image", + (actual.is_picture, actual.asset_hash), + (expected.is_picture, expected.asset_hash), + ), + ("group", len(actual.children), len(expected.children)), + ) + fields.extend( + field + for field, actual_value, expected_value in comparisons + if actual_value != expected_value + ) + return tuple(fields) + + +def _text_snapshot(obj: SceneObject) -> tuple[tuple[str, str, int, bool, bool], ...]: + return tuple( + (run.text, run.font_family, run.font_size, run.bold, run.italic) for run in obj.text_runs + ) + + def compare_slides( submission_dir: Path, gold_dir: Path, diff --git a/gloss-v1/grader/pyproject.toml b/gloss-v1/grader/pyproject.toml index df01303..3cb5494 100644 --- a/gloss-v1/grader/pyproject.toml +++ b/gloss-v1/grader/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "gloss" version = "1.0.0a1" -description = "Gloss benchmark grader — ECMA-376 slide generation fidelity testing" +description = "The open challenge and checker for native AI-made presentations" readme = "README.md" requires-python = ">=3.12" license = "Apache-2.0" diff --git a/gloss-v1/grader/tests/test_cli.py b/gloss-v1/grader/tests/test_cli.py index f105635..52d4fb4 100644 --- a/gloss-v1/grader/tests/test_cli.py +++ b/gloss-v1/grader/tests/test_cli.py @@ -4,6 +4,7 @@ import json from dataclasses import replace +from pathlib import Path from typing import TYPE_CHECKING, Any from click.testing import CliRunner @@ -21,8 +22,6 @@ ) if TYPE_CHECKING: - from pathlib import Path - import pytest @@ -275,3 +274,14 @@ def test_validate_exits_two_when_schema_cannot_run( assert result.exit_code == 2 assert "not performed" in result.output assert "schemas unavailable" in result.output + + +def test_check_reports_exact_gold_deck() -> None: + root = Path(__file__).resolve().parents[2] + gold = root / "benchmark" / "deck" / "gold" / "gloss-v1-gold.pptx" + + result = CliRunner().invoke(main, ["check", str(gold)]) + + assert result.exit_code == 0 + assert "Exact match" in result.output + assert "No native objects changed" in result.output diff --git a/gloss-v1/grader/tests/test_compare.py b/gloss-v1/grader/tests/test_compare.py index cacad89..5d775b0 100644 --- a/gloss-v1/grader/tests/test_compare.py +++ b/gloss-v1/grader/tests/test_compare.py @@ -2,15 +2,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING +import zipfile +from pathlib import Path import numpy as np +from lxml import etree from PIL import Image -from gloss.compare import compare_slides - -if TYPE_CHECKING: - from pathlib import Path +from gloss.compare import compare_native_decks, compare_slides def _image(path: Path, color: tuple[int, int, int], size: tuple[int, int] = (16, 16)) -> None: @@ -74,3 +73,72 @@ def test_diff_uses_signed_arithmetic_before_amplification(tmp_path: Path) -> Non assert result[0].diff_path == diffs / "diff-slide-01.png" with Image.open(result[0].diff_path) as diff: assert np.array(diff).max() == 255 + + +def _move_named_native_objects( + source: Path, + destination: Path, + targets: dict[str, str], +) -> None: + drawing_namespace = "http://schemas.openxmlformats.org/drawingml/2006/main" + presentation_namespace = "http://schemas.openxmlformats.org/presentationml/2006/main" + parser = etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False) + with zipfile.ZipFile(source) as original, zipfile.ZipFile(destination, "w") as mutated: + for info in original.infolist(): + content = original.read(info.filename) + if target_text := targets.get(info.filename): + root = etree.fromstring(content, parser=parser) + shape_tree = root.find(f".//{{{presentation_namespace}}}spTree") + assert shape_tree is not None + + def shape_text(candidate: etree._Element) -> str: + values = candidate.xpath( + ".//a:t/text()", + namespaces={"a": drawing_namespace}, + ) + assert isinstance(values, list) + return "".join(str(value) for value in values) + + shape = next( + ( + candidate + for candidate in shape_tree.findall(f"{{{presentation_namespace}}}sp") + if target_text in shape_text(candidate) + ), + None, + ) + assert shape is not None + offset = shape.find( + f"{{{presentation_namespace}}}spPr/" + f"{{{drawing_namespace}}}xfrm/{{{drawing_namespace}}}off" + ) + assert offset is not None + offset.set("x", str(int(offset.get("x", "0")) + 914_400)) + content = etree.tostring(root, xml_declaration=True, encoding="UTF-8") + mutated.writestr(info, content) + + +def test_native_check_gold_is_exact_and_two_moves_are_two_findings(tmp_path: Path) -> None: + root = Path(__file__).resolve().parents[2] + gold = root / "benchmark" / "deck" / "gold" / "gloss-v1-gold.pptx" + assert compare_native_decks(gold, gold) == [] + + edited = tmp_path / "edited.pptx" + _move_named_native_objects( + gold, + edited, + { + "ppt/slides/slide2.xml": "Agenda", + "ppt/slides/slide12.xml": "Document Fields", + }, + ) + + changes = compare_native_decks(edited, gold) + + assert len(changes) == 2 + assert [change.slide_number for change in changes] == [2, 12] + assert [change.label for change in changes] == [ + "placeholder “Agenda”", + "placeholder “Document Fields”", + ] + assert all(change.changed_fields == ("position",) for change in changes) diff --git a/gloss-v1/service/gloss_service/main.py b/gloss-v1/service/gloss_service/main.py index e67248e..6ccbe2e 100644 --- a/gloss-v1/service/gloss_service/main.py +++ b/gloss-v1/service/gloss_service/main.py @@ -205,8 +205,9 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]: title="Gloss hosted service", version="1.0.0", description=( - "Gloss — Generative Layout & OOXML Scoring System. Controlled grading and " - "public grading-verified leaderboard." + "Supporting measurement service for Gloss — the Generative Layout & " + "Object Structure Standard presentation challenge. Provides controlled " + "grading and a public grading-verified leaderboard." ), docs_url="/v1/docs", redoc_url="/v1/redoc", diff --git a/gloss-v1/service/gloss_service/static/index.html b/gloss-v1/service/gloss_service/static/index.html index ee8a60b..a2ad55e 100644 --- a/gloss-v1/service/gloss_service/static/index.html +++ b/gloss-v1/service/gloss_service/static/index.html @@ -1,10 +1,10 @@ - + - - Gloss — Generative Layout & OOXML Scoring System + + Gloss hosted checker and leaderboard @@ -26,7 +26,7 @@
-

Generative Layout & OOXML Scoring System

+

Supporting measurement for the Gloss challenge

Can your model make PowerPoint—or only pictures of it?

Twenty adversarial slides. Native tables, live charts, multilingual type, masters, groups, fields, and unforgiving geometry. Every published result is labeled exactly: grading-verified artifact score; generation-attested.

@@ -34,9 +34,9 @@

Can your model make PowerPoint—or only pictures o Read the API contract

-
+
-

Gloss measurement stack

+

Hosted checker stack

  1. 01Quarantine
  2. 02LibreOffice render
  3. @@ -44,7 +44,7 @@

    Can your model make PowerPoint—or only pictures o
  4. 04Weighted checklist

No screenshots. No traces. One final .pptx.

-
+
@@ -56,7 +56,7 @@

The fidelity board

Loading verified runs…
-
+
@@ -114,20 +114,20 @@

A score with receipts.

Bring the final deck.

Organizations receive an API key from a benchmark maintainer, register immutable model and revision keys, then precommit a campaign. Each upload carries only its campaign_id plus generation attestation; tier, variant, benchmark, and cohort cannot be overridden.

-
+
POST /v1/submissions
curl https://your-gloss-host/v1/submissions \
   -H "Authorization: Bearer $GLOSS_API_KEY" \
   -F 'metadata=@submission.json;type=application/json' \
   -F 'file=@model-output.pptx'
Open interactive API docs -
+
diff --git a/launch/README.md b/launch/README.md index db197d9..b8c6b35 100644 --- a/launch/README.md +++ b/launch/README.md @@ -1,15 +1,19 @@ -# Gloss launch assets +# Gloss launch surface -The launch page and film are static, repository-owned, and derived from committed evidence. +The launch page is static, repository-owned, and centered on the public 20-slide +challenge deck. Every slide image links to its exact instructions, and the primary +calls to action download the native deck, open the single master prompt, or join +the GitHub project. ## Evidence -`site/evidence/preview-v1.json` records candidate-harness counts. -`gloss-v1/benchmark/comparative-v1/summary.json` records the frozen -generation-path scores used by the central chart and film. Its public copy at -`site/evidence/comparative-v1-summary.json` must remain semantically identical. +`site/evidence/preview-v1.json` records candidate-harness counts for the +supporting measurement layer. The launch film is presentation-first: it uses +real Gloss v1 slide renders, the two-object checker example, the ACID lineage, +and the GitHub call to action. It does not lead with comparative scores. -Verify the evidence, local links, caveats, and media metadata: +Verify the deck, all twenty renders and prompt links, supporting evidence, local +assets, claims, and media metadata: ```bash node launch/verify-launch.mjs diff --git a/launch/launch-post.md b/launch/launch-post.md index 0d8c7fa..b408394 100644 --- a/launch/launch-post.md +++ b/launch/launch-post.md @@ -2,35 +2,45 @@ ## Primary post -A screenshot is not a PowerPoint. +Make the deck. Not a screenshot. -Gloss is an open benchmark for generated decks that need to look right **and** be built right: native shapes, charts, tables, layouts, text, assets, and OOXML relationships. +Gloss is one deliberately complicated 20-slide presentation that AI struggles +to make natively. Editable text. Live charts. Real tables. Masters, layouts, +fields, connectors, RTL type, rotation, groups, and off-canvas bleed. -The technical preview now includes a frozen comparative bundle: 12 editable decks, 240 canonically rendered slides, and every artifact and report on GitHub. Native generation paths scored 67.68% local fidelity; visual-first paths scored 62.32%. +Remember Acid1, Acid2, and Acid3? Gloss is ACID for presentation decks made by +AI: one hostile public artifact, visible failures, and a community that can fix +them. -Those are reproducible repository workflows, not model rankings. Every report is self-reported and carries no model attribution. +Download the native deck. Move two objects. `gloss check edited.pptx` reports +exactly two findings. Then give the exact prompt, reference images, and approved +assets to your presentation-generating AI and see whether it can make the real +thing. -That work is public. Break a case, tighten an assertion, or challenge the evidence: +The deck is the challenge. The checker and deeper measurement tools are the +support layer. PowerPoint is first; Google Slides and Keynote are part of the +mission. https://gloss.tools https://github.com/aronchick/gloss ## Short version -A screenshot is not a PowerPoint. +Make the deck. Not a screenshot. -Gloss checks whether generated decks look right and are built right — all the way down to native OOXML. +Gloss is a deliberately complicated 20-slide presentation that AI struggles to +make natively. It is ACID for presentation decks made by AI. -12 decks. 240 graded slides. A visible 7.37-point native-structure gap. No invented model leaderboard. The decks, reports, evidence, and unfinished release gates are public. +Download the deck. Copy the exact prompt. Build the next hard case with us. Build it with us: https://gloss.tools ## Video alt text -Square, silent launch film on a clean light background. It introduces Gloss, then animates local artifact-fidelity bars for four deterministic generation paths. A dark title card reveals a 7.37-point native-structure advantage for native construction. A second chart pairs visual SSIM in orange with native weighted pass rate in blue, showing similar pixels but different PowerPoint structure. The end card reads “Gloss — Generative Layout & OOXML Scoring System,” “12 decks / 240 slides,” and “gloss.tools.” The film labels every result as a repository-owned workflow baseline with no model attribution. +Square, silent launch film with an editorial cream, navy, cobalt, orange, and acid-green palette. It opens with “Make the deck. Not a screenshot,” then shows three real slides from the twenty-slide Gloss challenge inside presentation-editor frames. A native object is selected on the composite stress slide, followed by a terminal where moving two objects produces exactly two findings. The film connects Gloss to Acid1, Acid2, and Acid3, then ends with “Gloss — Generative Layout & Object Structure Standard,” gloss.tools, and the GitHub repository. ## Posting note Attach `site/media/gloss-launch.mp4`. If a platform strips video metadata or -recompresses the file, keep the public post copy unchanged; the source evidence -remains `gloss-v1/benchmark/comparative-v1/summary.json`. +recompresses the file, keep the public post copy unchanged. The renderer and +every reference slide remain committed in the repository. diff --git a/launch/render-video.mjs b/launch/render-video.mjs index 2dd0dd3..a71b519 100644 --- a/launch/render-video.mjs +++ b/launch/render-video.mjs @@ -1,23 +1,16 @@ #!/usr/bin/env node -import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; -import { dirname, join, resolve } from "node:path"; +import { dirname, extname, join, resolve } from "node:path"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const launchDir = dirname(fileURLToPath(import.meta.url)); const root = resolve(launchDir, ".."); -const summaryPath = join(root, "gloss-v1/benchmark/comparative-v1/summary.json"); const outputPath = join(root, "site/media/gloss-launch.mp4"); const posterPath = join(root, "site/media/gloss-launch-poster.png"); -const summary = JSON.parse(readFileSync(summaryPath, "utf8")); -const nativePaths = summary.paths.filter((path) => path.path_id.startsWith("native-")); -const visualPaths = summary.paths.filter((path) => path.path_id.startsWith("visual-")); -const mean = (paths, metric) => paths.reduce((total, path) => total + path.mean_metrics[metric], 0) / paths.length; -const nativePass = mean(nativePaths, "native_weighted_pass_percent"); -const visualPass = mean(visualPaths, "native_weighted_pass_percent"); -const nativeGap = nativePass - visualPass; +const exportsDir = join(root, "gloss-v1/benchmark/deck/exports"); const width = 1080; const height = 1080; @@ -25,16 +18,16 @@ const fps = 30; const duration = 21; const frameCount = fps * duration; const colors = { - canvas: "#f4f6fa", - paper: "#ffffff", - ink: "#171a22", - muted: "#68717f", - rule: "#c9ced8", - orange: "#d35230", - orangeSoft: "#f7ded5", + canvas: "#f2efe8", + paper: "#fffdf8", + ink: "#111923", + muted: "#66707b", + rule: "#c6c2b8", + orange: "#e45b35", + orangeSoft: "#f9ded4", blue: "#2859d6", blueSoft: "#dfe8ff", - proof: "#b7f36b", + acid: "#c8ff63", }; function commandExists(command) { @@ -42,9 +35,7 @@ function commandExists(command) { } function requireCommand(command) { - if (!commandExists(command)) { - throw new Error(`${command} is required to render the launch video`); - } + if (!commandExists(command)) throw new Error(`${command} is required to render the launch video`); } function clamp(value, min = 0, max = 1) { @@ -56,7 +47,7 @@ function ease(value) { return 1 - (1 - t) ** 3; } -function fadeInOut(t, start, end, edge = 0.4) { +function fadeInOut(t, start, end, edge = 0.35) { return clamp((t - start) / edge) * clamp((end - t) / edge); } @@ -85,9 +76,9 @@ function svgText(text, x, y, options = {}) { function mono(text, x, y, options = {}) { return svgText(text, x, y, { family: "SFMono-Regular, Consolas, Liberation Mono, monospace", - size: 22, + size: 19, weight: 700, - spacing: 1.5, + spacing: 1.2, ...options, }); } @@ -99,133 +90,170 @@ function baseSvg(body, background = colors.canvas) { `; } +function imageData(path) { + const mime = extname(path) === ".png" ? "image/png" : "image/jpeg"; + return `data:${mime};base64,${readFileSync(path).toString("base64")}`; +} + +const slides = { + cover: imageData(join(exportsDir, "slide-01.png")), + composite: imageData(join(exportsDir, "slide-13.png")), + final: imageData(join(exportsDir, "slide-20.png")), +}; + +function slideFrame(href, x, y, w, h, options = {}) { + const { active = false, label = "NATIVE SLIDE", index = "01" } = options; + const handle = 12; + const selection = active + ? ` + ${[ + [x + w * 0.55, y + h * 0.17], + [x + w * 0.84, y + h * 0.17], + [x + w * 0.55, y + h * 0.65], + [x + w * 0.84, y + h * 0.65], + ].map(([hx, hy]) => ``).join("")}` + : ""; + return ` + + + + + ${mono(`${index} / ${label}`, x + 58, y - 17, { size: 12, fill: colors.muted })} + + ${selection} + `; +} + function frameIntro(t) { - const p = ease(t / 1.5); - const opacity = fadeInOut(t, 0, 2, 0.35); - const offset = 40 * (1 - p); + const p = ease(t / 1.25); + const opacity = fadeInOut(t, 0, 3.2, 0.3); return baseSvg(` - - ${mono("OPEN POWERPOINT BENCHMARK", 72, 112, { fill: colors.orange })} - ${svgText("Gloss", 72, 480, { size: 230, weight: 780 })} - - ${svgText("Same file.", 76, 655, { size: 78, weight: 720 })} - ${svgText("Two truths.", 76, 742, { size: 78, weight: 720, fill: colors.blue })} - ${mono("LOOKS RIGHT ↔ BUILT RIGHT", 76, 928, { fill: colors.muted })} + + ${mono("GLOSS / AN OPEN ACID TEST", 72, 108, { fill: colors.orange })} + ${svgText("Make the deck.", 72, 430, { size: 116, weight: 780 })} + ${svgText("Not a screenshot.", 72, 552, { size: 116, weight: 780, fill: colors.blue })} + + ${svgText("One deliberately complicated presentation", 76, 744, { size: 50, weight: 650 })} + ${svgText("that AI struggles to make natively.", 76, 807, { size: 50, weight: 650 })} + ${mono("POWERPOINT · GOOGLE SLIDES · KEYNOTE", 76, 974, { fill: colors.muted })} `); } -function frameTwoLayers(t) { - const local = t - 2; - const opacity = fadeInOut(t, 2, 9.2, 0.35); - const rows = summary.paths.map((path, index) => ({ - label: path.label.toUpperCase(), - value: path.mean_metrics.local_fidelity_percent, - color: path.path_id.startsWith("native-") ? colors.blue : colors.orange, - y: 430 + index * 130, - })); - const rowMarkup = rows.map((row, index) => { - const stagger = ease((local - index * 0.32) / 3.4); - const barWidth = row.value / 100 * 720 * stagger; - const score = (row.value * stagger).toFixed(2); - return ` - ${mono(row.label, 78, row.y - 28, { fill: colors.muted, size: 19 })} - - - ${svgText(score, 950, row.y + 51, { size: 48, weight: 760, anchor: "end" })} - `; - }).join(""); +function frameDeck(t) { + const local = t - 3.2; + const opacity = fadeInOut(t, 3.2, 8, 0.3); + const p = ease(local / 1.1); return baseSvg(` - ${mono("GLOSS / FROZEN COMPARATIVE V1", 78, 90, { fill: colors.orange })} - ${svgText("Local artifact", 78, 220, { size: 100, weight: 760 })} - ${svgText("fidelity.", 78, 320, { size: 100, weight: 760 })} - ${rowMarkup} - ${mono(summary.disclosure.verification_label.toUpperCase(), 78, 1002, { fill: colors.muted, size: 17 })} + ${mono("THE CHALLENGE IS THE PRESENTATION", 64, 78, { fill: colors.orange })} + ${svgText("20 slides.", 64, 182, { size: 78, weight: 780 })} + ${svgText("No easy outs.", 64, 260, { size: 78, weight: 780, fill: colors.blue })} + + ${slideFrame(slides.cover, 64, 360, 500, 281, { index: "01" })} + ${slideFrame(slides.composite, 518, 558, 500, 281, { index: "13", label: "COMPOSITE STRESS" })} + ${slideFrame(slides.final, 64, 754, 500, 281, { index: "20", label: "FINAL TORTURE" })} + + ${mono("CHARTS · TABLES · FIELDS · MASTERS · RTL · GROUPS", 1014, 1025, { anchor: "end", size: 15, fill: colors.muted })} `); } -function frameTwist(t) { - const local = t - 9.2; - const opacity = fadeInOut(t, 9.2, 11, 0.2); - const p = ease(local / 1.2); - const gap = (nativeGap * p).toFixed(2); +function frameNative(t) { + const local = t - 8; + const opacity = fadeInOut(t, 8, 12.4, 0.3); + const p = ease(local / 1.4); + const list = ["LIVE CHART", "NATIVE TABLE", "EDITABLE TEXT", "REAL CONNECTIONS"]; return baseSvg(` - - - ${mono("THE ARTIFACT TWIST", 92, 130, { fill: colors.proof })} - ${svgText(`+${gap}`, 540, 535, { size: 280, weight: 790, fill: colors.paper, anchor: "middle" })} - ${svgText("points of native structure", 540, 670, { size: 62, weight: 700, fill: colors.paper, anchor: "middle" })} - ${svgText("from native construction.", 540, 744, { size: 62, weight: 700, fill: colors.blueSoft, anchor: "middle" })} - ${mono("SIMILAR PIXELS · DIFFERENT POWERPOINT", 540, 927, { fill: "#aab2c0", anchor: "middle", size: 19 })} + ${mono("LOOK UNDER THE PIXELS", 64, 76, { fill: colors.orange })} + ${slideFrame(slides.composite, 64, 182, 678, 381, { active: true, index: "13", label: "COMPOSITE STRESS" })} + + ${svgText("Everything", 64, 700, { size: 86, weight: 780 })} + ${svgText("stays editable.", 64, 786, { size: 86, weight: 780, fill: colors.blue })} + ${list.map((item, index) => `${mono(`0${index + 1}`, 70 + (index % 2) * 465, 890 + Math.floor(index / 2) * 76, { fill: colors.orange })}${svgText(item, 120 + (index % 2) * 465, 891 + Math.floor(index / 2) * 76, { size: 24, weight: 720 })}`).join("")} + - `, colors.ink); + `); } -function frameMethods(t) { - const local = t - 11; - const opacity = fadeInOut(t, 11, 18.2, 0.3); - const rows = summary.paths.map((path, index) => { - const y = 400 + index * 135; - const p = ease((local - index * 0.22) / 3.5); - const visual = path.mean_metrics.mean_visual_ssim_percent; - const native = path.mean_metrics.native_weighted_pass_percent; - return ` - ${mono(path.label.toUpperCase(), 76, y - 22, { fill: colors.muted, size: 18 })} - - - - - ${mono(visual.toFixed(2), 952, y + 25, { anchor: "end", fill: colors.orange, size: 17 })} - ${mono(native.toFixed(2), 952, y + 67, { anchor: "end", fill: colors.blue, size: 17 })} - `; - }).join(""); +function frameCheck(t) { + const local = t - 12.4; + const opacity = fadeInOut(t, 12.4, 15.8, 0.25); + const cursor = clamp(local / 0.7); + const findings = clamp((local - 0.9) / 0.7); return baseSvg(` - ${mono("ORANGE = VISUAL SSIM · BLUE = NATIVE PASS", 76, 82, { fill: colors.blue })} - ${svgText("Pixels cluster.", 76, 202, { size: 88, weight: 760 })} - ${svgText("Structure splits.", 76, 292, { size: 88, weight: 760 })} - ${rows} - ${mono(`${summary.totals.runs} RUNS · ${summary.totals.slides} GRADED SLIDES · THREE PUBLIC SEEDS`, 76, 1020, { fill: colors.muted, size: 17 })} + ${mono("THE CHECKER IS THE SUPPORT LAYER", 70, 92, { fill: colors.orange })} + ${svgText("Move two things.", 70, 226, { size: 86, weight: 780 })} + ${svgText("See two things.", 70, 312, { size: 86, weight: 780, fill: colors.blue })} + + + + ${mono("$ gloss check edited.pptx", 110, 536, { size: 24, fill: colors.paper, opacity: cursor })} + + ${mono("2 native objects changed:", 110, 632, { size: 23, fill: colors.acid })} + ${mono("Slide 02 · Agenda · position", 110, 708, { size: 21, fill: "#cdd4df" })} + ${mono("Slide 12 · Document Fields · position", 110, 766, { size: 21, fill: "#cdd4df" })} + + ${mono("MEASUREMENT MADE EASY · THE DECK REMAINS THE POINT", 70, 978, { fill: colors.muted, size: 16 })} + + `); +} + +function frameAcid(t) { + const local = t - 15.8; + const opacity = fadeInOut(t, 15.8, 18.4, 0.25); + const p = ease(local / 1.1); + return baseSvg(` + + ${mono("REMEMBER ACID?", 74, 96, { fill: colors.orange })} + ${svgText("One hostile", 74, 342, { size: 112, weight: 780 })} + ${svgText("public artifact.", 74, 454, { size: 112, weight: 780, fill: colors.blue })} + + ${svgText("Visible failures.", 76, 640, { size: 64, weight: 720 })} + ${svgText("A community that fixes them.", 76, 714, { size: 64, weight: 720 })} + ${mono("ACID1", 76, 918, { fill: colors.muted })} + ${mono("ACID2", 288, 918, { fill: colors.muted })} + ${mono("ACID3", 500, 918, { fill: colors.muted })} + ${mono("GLOSS", 848, 918, { fill: colors.orange })} + `); } function frameEnd(t) { - const local = t - 18.2; + const local = t - 18.4; const p = ease(local / 1.1); const opacity = clamp(local / 0.25); return baseSvg(` - - ${mono("OPEN TECHNICAL PREVIEW", 74, 92, { fill: colors.proof })} - ${svgText("Gloss", 72, 388, { size: 230, weight: 790, fill: colors.paper })} - - ${svgText("Generative Layout &", 76, 545, { size: 50, weight: 690, fill: "#cdd3df" })} - ${svgText("OOXML Scoring System", 76, 607, { size: 50, weight: 690, fill: "#cdd3df" })} - - ${svgText(`${summary.totals.runs} decks / ${summary.totals.slides} slides`, 76, 795, { size: 62, weight: 780, fill: colors.proof })} - ${mono(summary.disclosure.attribution.toUpperCase(), 76, 837, { fill: "#aeb6c4", size: 15 })} - ${svgText("gloss.tools", 76, 982, { size: 72, weight: 760, fill: colors.paper })} - ${mono("BUILD IT WITH US ON GITHUB ↗", 1004, 982, { fill: colors.orange, anchor: "end", size: 17 })} + + ${mono("ACID FOR PRESENTATION DECKS MADE BY AI", 74, 94, { fill: colors.acid })} + ${svgText("Gloss", 70, 380, { size: 232, weight: 790, fill: colors.paper })} + + ${svgText("Generative Layout &", 76, 548, { size: 49, weight: 690, fill: "#d4dae3" })} + ${svgText("Object Structure Standard", 76, 608, { size: 49, weight: 690, fill: "#d4dae3" })} + + ${svgText("Download the deck.", 76, 782, { size: 51, weight: 760, fill: colors.paper })} + ${svgText("Copy the prompt. Build it with us.", 76, 845, { size: 51, weight: 760, fill: colors.paper })} + ${svgText("gloss.tools", 76, 988, { size: 72, weight: 760, fill: colors.paper })} + ${mono("GITHUB.COM/ARONCHICK/GLOSS ↗", 1004, 982, { fill: colors.orange, anchor: "end", size: 16 })} `, colors.ink); } function renderFrame(time) { - if (time < 2) return frameIntro(time); - if (time < 9.2) return frameTwoLayers(time); - if (time < 11) return frameTwist(time); - if (time < 18.2) return frameMethods(time); + if (time < 3.2) return frameIntro(time); + if (time < 8) return frameDeck(time); + if (time < 12.4) return frameNative(time); + if (time < 15.8) return frameCheck(time); + if (time < 18.4) return frameAcid(time); return frameEnd(time); } function run(command, args, options = {}) { const result = spawnSync(command, args, { encoding: "utf8", ...options }); - if (result.status !== 0) { - throw new Error(`${command} failed\n${result.stderr || result.stdout}`); - } + if (result.status !== 0) throw new Error(`${command} failed\n${result.stderr || result.stdout}`); return result; } @@ -259,8 +287,8 @@ try { "-crf", "18", "-pix_fmt", "yuv420p", "-movflags", "+faststart", - "-metadata", "title=Gloss — Generative Layout & OOXML Scoring System", - "-metadata", `comment=Rendered from ${summary.cohort.scoring_cohort_id}`, + "-metadata", "title=Gloss — Make the deck, not a screenshot", + "-metadata", "comment=Rendered from the public Gloss v1 presentation challenge", "-an", outputPath, ]); diff --git a/launch/verify-launch.mjs b/launch/verify-launch.mjs index 3dc1558..255e418 100644 --- a/launch/verify-launch.mjs +++ b/launch/verify-launch.mjs @@ -29,6 +29,10 @@ const robots = readFileSync(join(site, "robots.txt"), "utf8"); const sitemap = readFileSync(join(site, "sitemap.xml"), "utf8"); const siteScript = readFileSync(join(site, "site.js"), "utf8"); const videoRenderer = readFileSync(join(root, "launch/render-video.mjs"), "utf8"); +const launchPost = readFileSync(join(root, "launch/launch-post.md"), "utf8"); +const goldDeckPath = join(root, "gloss-v1/benchmark/deck/gold/gloss-v1-gold.pptx"); +const masterPromptPath = join(root, "gloss-v1/benchmark/prompts/DESIGNER_BRIEF.md"); +const exportDir = join(root, "gloss-v1/benchmark/deck/exports"); function assert(condition, message) { if (!condition) throw new Error(message); @@ -54,6 +58,10 @@ const requirementCounts = requirements.requirements.reduce((counts, requirement) }, {}); assert(evidence.status === "technical_preview", "Launch evidence must remain labeled technical_preview"); +assert( + evidence.product_retronym === "Generative Layout & Object Structure Standard", + "Gloss retronym drifted", +); assert(evidence.operator_evidence.release_evidence_claimed === 0, "Preview must not claim release evidence"); assert(evidence.candidate_checks.total === index.entries.length, "Candidate-check count drifted from fixture index"); assert(evidence.candidate_checks.visual_render === visual, "Visual-check count drifted from fixture index"); @@ -78,8 +86,6 @@ assert( for (const method of evidence.verification_methods) { assert(method.count === methodCounts[method.id], `Verification method ${method.id} drifted from fixture index`); - const htmlLabel = method.public_label.replaceAll("&", "&"); - assert(html.includes(`${htmlLabel}`) && html.includes(`${method.count}`), `Homepage method count is missing for ${method.id}`); } const sourcePaths = new Map([ @@ -95,22 +101,40 @@ for (const source of evidence.sources) { } const requiredCopy = [ - "A screenshot is not a", - "Open technical preview", - "0 official model results", - "The repository is the product", - "Working harness. Unfrozen benchmark.", - "Four public baselines. Zero model claims.", - "Inspect every deck and report", + "Make the deck.", + "Not a screenshot.", + "Gloss is ACID for presentation decks made by AI.", + "Move two things.", + "Twenty ways to fake a slide.", + "One exact prompt.", + "Native presentations, everywhere.", + "This deck may become training data.", + "Gloss lives on GitHub", ]; for (const copy of requiredCopy) assert(html.includes(copy), `Homepage is missing required copy: ${copy}`); assert(!/official leaderboard[^<]{0,80}(live|launched|ready)/i.test(html), "Homepage overclaims official leaderboard readiness"); assert(html.includes("https://github.com/aronchick/gloss"), "Homepage must lead to GitHub"); assert(html.includes(''), "Homepage canonical URL is missing"); assert(html.includes('"codeRepository": "https://github.com/aronchick/gloss"'), "Homepage structured data must lead to GitHub"); -assert(siteScript.includes('fetch("/evidence/comparative-v1-summary.json")'), "Homepage chart must load the frozen comparative summary"); -assert(videoRenderer.includes("gloss-v1/benchmark/comparative-v1/summary.json"), "Launch video must load the frozen comparative summary"); -assert(!videoRenderer.includes("site/evidence/preview-v1.json"), "Launch video must not source comparative claims from preview evidence"); +assert(siteScript.includes("inspectorContent"), "Homepage native-object inspector is missing"); +assert(siteScript.includes("IntersectionObserver"), "Homepage slide navigation observer is missing"); +assert(!siteScript.includes("comparative-v1-summary.json"), "Homepage must not lead with comparative scoring data"); +assert(videoRenderer.includes("THE CHALLENGE IS THE PRESENTATION"), "Launch film must lead with the presentation challenge"); +assert(videoRenderer.includes("THE CHECKER IS THE SUPPORT LAYER"), "Launch film must keep measurement in a supporting role"); +assert(videoRenderer.includes("slide-13.png"), "Launch film must use the real composite-stress slide"); +assert(!videoRenderer.includes("comparative-v1/summary.json"), "Launch film must not be driven by comparative scores"); +assert(launchPost.includes("Make the deck. Not a screenshot."), "Launch post is missing the presentation-first message"); +assert(launchPost.includes("The deck is the challenge."), "Launch post must state the product hierarchy"); +assert(existsSync(goldDeckPath), "Downloadable Gloss v1 deck is missing"); +assert(existsSync(masterPromptPath), "Exact master prompt is missing"); +assert(html.includes("gloss-v1-gold.pptx"), "Homepage deck download is missing"); +assert(html.includes("prompts/DESIGNER_BRIEF.md"), "Homepage exact-prompt link is missing"); +for (let slide = 1; slide <= 20; slide += 1) { + const number = String(slide).padStart(2, "0"); + assert(existsSync(join(exportDir, `slide-${number}.png`)), `Reference render ${number} is missing`); + assert(html.includes(`deck/exports/slide-${number}.png`), `Homepage image ${number} is missing`); + assert(html.includes(`canonical/slide-${number}.md`), `Homepage instructions ${number} are missing`); +} assert(robots.includes("Allow: /") && robots.includes("Sitemap: https://gloss.tools/sitemap.xml"), "robots.txt must expose the public site map"); assert(sitemap.includes("https://gloss.tools/"), "sitemap.xml must contain the production homepage"); for (const header of [ @@ -136,7 +160,7 @@ assert(statSync(posterPath).size > 20_000, "Launch poster is missing or unexpect const probe = spawnSync("ffprobe", [ "-v", "error", - "-show_entries", "stream=codec_type,width,height,avg_frame_rate:format=duration", + "-show_entries", "stream=codec_type,width,height,avg_frame_rate:format=duration:format_tags=title,comment", "-of", "json", videoPath, ], { encoding: "utf8" }); @@ -148,5 +172,6 @@ assert(video?.width === 1080 && video?.height === 1080, "Launch video must be 10 assert(video?.avg_frame_rate === "30/1", "Launch video must be 30 fps"); assert(Math.abs(Number(metadata.format.duration) - 21) < 0.05, "Launch video must be 21 seconds"); assert(!audio, "Launch video must be silent and contain no audio stream"); +assert(metadata.format.tags?.title === "Gloss — Make the deck, not a screenshot", "Launch video title metadata drifted"); process.stdout.write(`Launch verification: PASS\n- evidence: ${evidence.evidence_id}\n- candidate checks: ${index.entries.length}\n- positive controls: ${positivePassed}/${report.results.length}\n- mutants detected: ${mutantsDetected}/${report.results.length}\n- site assets: present\n- video: 1080x1080, 30 fps, 21 seconds, silent\n`); diff --git a/site/evidence/preview-v1.json b/site/evidence/preview-v1.json index cd8798d..ff0c31e 100644 --- a/site/evidence/preview-v1.json +++ b/site/evidence/preview-v1.json @@ -2,7 +2,7 @@ "schema_version": "1.0", "evidence_id": "gloss-technical-preview-2026-07-22", "product_name": "Gloss", - "product_retronym": "Generative Layout & OOXML Scoring System", + "product_retronym": "Generative Layout & Object Structure Standard", "protocol_id": "gloss-v1", "status": "technical_preview", "status_label": "Technical preview — not an official leaderboard", @@ -106,7 +106,7 @@ }, { "path": "gloss-v1/benchmark/requirements/prompt-requirements.json", - "sha256": "58195dc3db2489a60a79786e27ab2392ad2f617f11d4ca619247389ac9124367" + "sha256": "ef238f041afe52e913a66694a335b3959064c23ec1100c703365a314dbd7b37e" } ] } diff --git a/site/index.html b/site/index.html index 93321f7..ac15596 100644 --- a/site/index.html +++ b/site/index.html @@ -1,52 +1,52 @@ - + - + - + - - - + + + - Gloss — a screenshot is not a PowerPoint + Gloss — make the deck, not a screenshot - + + @@ -54,233 +54,259 @@
-

Open technical preview

-

A screenshot is not a PowerPoint.

-

Gloss is a deliberately difficult presentation deck for AI to render and build as a real, editable PowerPoint. Its open measurement tools show whether the result looks right and is built right.

+

An open ACID test for AI-made presentations

+

Make the deck.
Not a screenshot.

+

Gloss is a deliberately complicated 20-slide presentation that AI struggles to recreate as a real, editable artifact. The deck is the challenge. The checker makes every native miss easy to see.

-

Generative Layout & OOXML Scoring System · Apache-2.0

+

Remember Acid1, Acid2, and Acid3? Gloss is ACID for presentation decks made by AI.

-
-
-
- - -
- submission.pptx +
+
+ + gloss-v1.pptx + Slide 01 / 20
-
-
-
-
Q3 / SYSTEMS
-
The artifact
is the product.
-
-
-
Rendered pixels
+
+
+
+ Gloss v1 cover slide with layered translucent shapes +
-

108 visual checks inspect what reaches the canvas.

- -
Gloss artifact X-raycanvas ↔ package
-
- - Evidence bundle - Public JSON - Open the receipts ↗ - -
- Candidate checks - 280 - 108 visual · 172 inside-file -
-
- Positive controls - 280/280 - passed -
-
- Single-fault mutations - 280/280 - detected +
+

Looks right is not enough.

+ Text stays text. Charts stay charts. Masters still master. +
+ +
+
+

The smallest useful experiment

+

Move two things.
See two things.

+

Download the native deck, move one object on each of two slides, and run the checker. Gloss reports those two native objects—not a score, not a dashboard, not a wall of release metadata.

-
- Release state - Preview - 0 official model results +
+
+ 1 +

Download

+

Start with the public, editable Gloss v1 deck.

+ Get the .pptx ↓ +
+
+ 2 +

Edit

+

Move one object on one slide and another object elsewhere.

+ Keep everything native. +
+
+ 3 +

Check

+

One changed object becomes one clear finding.

+ uv run gloss check edited.pptx +
+
+
gloss check
+
uv run gloss check edited.pptx
+
2 native objects changed:
Slide 02 · placeholder “Agenda” · changed: position
Slide 12 · placeholder “Document Fields” · changed: position
+

+
-
-
+
+
-

Frozen comparative v1

-

Four public baselines. Zero model claims.

-
-
-

Twelve editable decks, 240 graded slides, and three seeds per path. These are deterministic repository workflows built to expose the difference between visual similarity and native PowerPoint structure.

- +

The artifact is the benchmark

+

Twenty ways to fake a slide.
Twenty ways Gloss notices.

+

Every slide pairs a visible composition with a native behavior underneath. Open the image to see the target. Open the instructions to see exactly what the presentation must understand.

-
-
- - comparative-v1 / summary.json - 12 runs · 240 slides -
-
-

Loading frozen baseline scores…

-
-
- local artifact score; self-reported - cohort loading + +
- -
+ -
-
-

The 21-second version

-

The native gap, in 21 seconds.

-

The film is rendered from the frozen comparative summary above. These are repository-owned workflow baselines, not model scores.

-
-
- -
00:211080 × 1080silentview renderer ↗
+
+
+ Slide 1, Gloss v1 cover +
01 · Foundation

Cover / Title Stress Test

A cinematic title composition with an image and translucent shapes.

What must be native

Layout placeholders, image crop, z-order, opacity, shadow, and inherited slide numbers.

Read the exact instructions →
+
+
+ Slide 2, dense agenda +
02 · Foundation

Dense Agenda with Layout Semantics

A dense agenda that still reads cleanly.

What must be native

Placeholders, real bullet levels, paragraph spacing and indentation, flat grouping, and exact alignment.

Read the exact instructions →
+
+
+ Slide 3, performance metrics table +
03 · Foundation

Native Table Stress Test

A formatted performance table with a connected annotation.

What must be native

A real table—not a pile of rectangles—plus cell styles, mixed borders, paragraph indents, and a connector.

Read the exact instructions →
+
+
+ Slide 4, revenue growth chart +
04 · Foundation

Native Chart Stress Test

A data-rich chart with annotations and takeaways.

What must be native

An editable chart with correct data, type, labels, gridlines, legend, overlay shapes, and arrow connectors.

Read the exact instructions →
+
+
+ Slide 5, team cards +
05 · Foundation

Master Reuse Enforcement

A repeatable three-card team system.

What must be native

Master inheritance, grouped cards, equal distribution, placeholders, and a footer inherited instead of copied.

Read the exact instructions →
+
+
+ Slide 6, multilingual editorial layout +
06 · Systems

Multilingual Editorial

English, Arabic, and Japanese sharing one editorial canvas.

What must be native

RTL direction, CJK line breaking, script-specific fonts, exact Unicode text, and cross-column overlap.

Read the exact instructions →
+
+
+ Slide 7, image handling +
07 · Systems

Image Crop and Mask

Three treatments of approved source imagery.

What must be native

Independent crops, circle and rounded-rectangle masks, asset identity, and a correctly layered overlay.

Read the exact instructions →
+
+
+ Slide 8, depth and layering +
08 · Systems

Overlap, Shadow, and Transparency

A cascading stack with convincing depth.

What must be native

Precise z-order, five opacity levels, shadows, selective grouping, and a real gradient fill.

Read the exact instructions →
+
+
+ Slide 9, API reference +
09 · Systems

Dense Text Overflow

Two code-heavy columns with deliberately different overflow behavior.

What must be native

Clipping versus auto-shrink, whitespace, monospaced runs, exact indentation, and line spacing.

Read the exact instructions →
+
+
+ Slide 10, system architecture +
10 · Systems

Connector and Alignment Diagram

A legible systems diagram built from editable objects.

What must be native

Attached connectors, elbow routing, arrowheads, grid alignment, labels, and nested backend groups.

Read the exact instructions →
+
+
+ Slide 11, brand colors +
11 · Systems

Theme vs. Local Override

Two rows of swatches that look almost interchangeable.

What must be native

Theme-linked colors versus explicit RGB overrides—the invisible difference a screenshot cannot prove.

Read the exact instructions →
+
+
+ Slide 12, document fields +
12 · Systems

Native Field Slide

Live document metadata beside a static lookalike.

What must be native

Slide-number and date fields, a master footer field, and the difference between a field and typed text.

Read the exact instructions →
+
+
+ Slide 13, composite stress +
13 · Torture

Composite Stress

Chart, table, image, Arabic annotation, and dense callouts in one composition.

What must be native

Multiple editable object types and their relationships surviving together.

Read the exact instructions →
+
+
+ Slide 14, RTL systems review +
14 · Torture

RTL-Heavy Comparison

A mirrored Arabic and English comparison.

What must be native

RTL paragraphs, mixed bidirectional runs, script-aware fonts, and mirrored alignment without flattening.

Read the exact instructions →
+
+
+ Slide 15, rotation atlas +
15 · Torture

Rotated Text

Typography at 0°, 45°, 90°, 135°, and 270°.

What must be native

Real rotation values, anchor points, bounding boxes, and shapes aligned to rotated text.

Read the exact instructions →
+
+
+ Slide 16, beyond the frame +
16 · Torture

Intentional Off-Canvas Bleed

Design elements that deliberately continue beyond the canvas.

What must be native

Negative coordinates and overhanging geometry without clipping or “fixing” the intentional overflow.

Read the exact instructions →
+
+
+ Slide 17, nested systems +
17 · Torture

Deep Grouping

A composition built from nested visual systems.

What must be native

Three group levels, exact membership, child transforms, and z-order inside each level.

Read the exact instructions →
+
+
+ Slide 18, three cities editorial +
18 · Torture

Multi-Column Editorial

A three-column English and Japanese magazine layout.

What must be native

Column structure, repeated image treatments, pull quotes, mixed-script text, and a patterned background.

Read the exact instructions →
+
+
+ Slide 19, design system audit +
19 · Torture

Repetition and Consistency

The deck’s design language turned into a consistency audit.

What must be native

Master-derived typography, colors, spacing, line weights, and reusable layout behavior.

Read the exact instructions →
+
+
+ Slide 20, Gloss synthesis +
20 · Torture

Final Torture Slide

The entire challenge in one dense synthesis.

What must be native

Chart, table, crop, multilingual text, rotation, transparency, connectors, fields, groups, and master inheritance.

Read the exact instructions →
+
-
-
-

One file. Two truths.

-

Visual fidelity is necessary. Native structure is non-negotiable.

-

A generated deck can match the reference image and still collapse the moment someone edits a chart, applies a layout, changes a theme, or reads the file with another tool. Gloss grades both layers.

-

Remember Acid1 and Acid2? Gloss is ACID for AI-made presentation decks.

- Read the open scoring contract -
-
-
Rendered pixels108
-
The artifact twistinside the .pptx
-
Object structure101
-
Layouts & masters25
-
Package safety22
-
Text semantics20
-
Asset hashes4
-

Candidate checks by method. Counts come from preview-v1.json.

+
+
+

Now give it to an AI

+

One exact prompt.
Nothing hidden.

+

The complete 20-slide brief is assembled in one copyable file. Give it to any presentation-generating AI with the reference images, approved assets, and fonts. Ask for a native, editable presentation. Do not give it the gold deck.

+
+
+
DESIGNER_BRIEF.md20 slides · public
+
# Gloss v1 — Canonical Deck Authoring Brief
+
+Build a real, editable presentation.
+
+You MUST create the masters and layouts
+before building slides.
+
+Native objects required:
+  charts · tables · fields · connectors
+  groups · placeholders · pictures
+
+Reference images are supplementary.
+The natural-language requirements win.
+
-
-
-
-

The repository is the product

-

Find a weak assumption. Open a patch.

-

No waitlist and no private benchmark committee. Cases, assertions, evidence, and release gates are inspectable on GitHub.

-
- - +
+
+

The principle is bigger than one file type

+

Native presentations, everywhere.

+
+
+

Gloss is about native presentations across PowerPoint, Google Slides, and Keynote. A chart should remain a chart. Text should remain text. Layout behavior should survive the next person editing the deck.

+

V1 is honestly PPTX-first. OOXML is a published standard with an inspectable package and object structure, so independent local checking is possible today. The same artifact-first contract should extend to other presentation systems as reliable adapters mature.

+ Help define the next adapter →
-
-
-

Truth before leaderboard

-

Working harness. Unfrozen benchmark.

-

Gloss is ready for public collaboration, not official model ranking. The release validator fails closed until independent evidence and frozen artifacts exist.

-
-
-
Candidate corpus validates

280 schema-valid checks and 381 traceable prompt clauses.

passing
-
Mutation operators execute

280 controls pass; 280 generated single-fault mutations are detected.

passing
-
Independent evidence

Assertion provenance and evidence remain incomplete for 280 candidate checks.

open
-
Comparative bundle

12 local runs, 240 slides, exact artifact hashes, and a one-command verifier are frozen.

passing
-
Official release

Independent prompt convergence, reviewer approvals, and signed release indexes remain open.

open
+
+ +
+

Public today, harder tomorrow

+

This deck may become training data. Good.

+

Gloss v1 is a shared conformance target, not a permanent secret exam. Future generations need automated challenge creation, delayed disclosure, rotating prompts, and held-back decks that measure generalization. We are publishing the starting point so the community can build that system in the open.

-
+
-

Reproduce it locally

-

Trust the command, not the claim.

+

Gloss lives on GitHub

+

Find the next thing AI fakes.

+

Propose a hard presentation behavior, reproduce a checker gap, challenge the evidence, or improve the prompt. The useful work happens in issues and pull requests.

-
-
Terminal
-
git clone https://github.com/aronchick/gloss.git
-cd gloss
-./gloss-v1/benchmark/comparative-v1/reproduce.sh
-

+
- -
-

Gloss is built in public

-

Make generated decks worth editing.

-
- Join on GitHub +

A screenshot is not a presentation

Make the artifact real.

+ Open Gloss on GitHub ↗
diff --git a/site/media/gloss-launch-poster.png b/site/media/gloss-launch-poster.png index 0742ee9..09bab27 100644 Binary files a/site/media/gloss-launch-poster.png and b/site/media/gloss-launch-poster.png differ diff --git a/site/media/gloss-launch.mp4 b/site/media/gloss-launch.mp4 index acc936a..ee312eb 100644 Binary files a/site/media/gloss-launch.mp4 and b/site/media/gloss-launch.mp4 differ diff --git a/site/site.css b/site/site.css index 33f29b4..8b3b26d 100644 --- a/site/site.css +++ b/site/site.css @@ -1,416 +1,230 @@ :root { - --canvas: #f4f6fa; + --ink: #111923; + --ink-soft: #1b2634; --paper: #ffffff; - --ink: #171a22; - --muted: #626a79; - --rule: #c9ced8; - --orange: #d35230; - --orange-soft: #f7ded5; - --package: #2859d6; - --package-soft: #dfe8ff; - --proof: #b7f36b; - --display: "Aptos Display", "Arial Narrow", "Segoe UI", sans-serif; - --body: "Aptos", "Segoe UI", Helvetica, Arial, sans-serif; + --canvas: #e8eaee; + --canvas-deep: #d9dce2; + --muted: #5f6875; + --rule: #c7cbd2; + --coral: #e8634a; + --teal: #2aacb8; + --gold: #d4a843; + --blue: #456ff2; + --shell: min(94rem, calc(100vw - 3rem)); + --display: "Avenir Next Condensed", "Arial Narrow", "Helvetica Neue", sans-serif; + --body: "Avenir Next", "Helvetica Neue", Arial, sans-serif; --mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; - --shell: min(92rem, calc(100vw - 3rem)); } * { box-sizing: border-box; } -html { scroll-behavior: smooth; } -body { - margin: 0; - background: var(--canvas); - color: var(--ink); - font: 400 17px/1.55 var(--body); - -webkit-font-smoothing: antialiased; -} -button, input { font: inherit; } +html { scroll-behavior: smooth; scroll-padding-top: 8rem; } +body { margin: 0; background: var(--canvas); color: var(--ink); font: 400 17px/1.58 var(--body); } +body::before { content: ""; position: fixed; z-index: 100; inset: 0 auto 0 0; width: .38rem; background: var(--coral); } +img { max-width: 100%; } a { color: inherit; } -svg { display: block; } -[hidden] { display: none !important; } +button, input { font: inherit; } +button:focus-visible, a:focus-visible { outline: 3px solid var(--blue); outline-offset: 4px; } -.skip-link { - position: fixed; - z-index: 100; - top: 1rem; - left: 1rem; - padding: .75rem 1rem; - background: var(--ink); - color: white; - transform: translateY(-200%); -} +.skip-link { position: fixed; z-index: 1000; top: .75rem; left: 1rem; padding: .7rem 1rem; background: var(--paper); color: var(--ink); transform: translateY(-160%); } .skip-link:focus { transform: none; } -:focus-visible { outline: 3px solid var(--package); outline-offset: 4px; } -.site-header { - position: relative; - z-index: 10; - display: flex; - align-items: center; - justify-content: space-between; - gap: 2rem; - width: var(--shell); - margin: 0 auto; - min-height: 5rem; - border-bottom: 1px solid var(--rule); -} -.wordmark { - display: inline-flex; - align-items: center; - gap: .7rem; - color: var(--ink); - font: 750 1.35rem/1 var(--display); - letter-spacing: -.045em; - text-decoration: none; -} -.wordmark-icon { - position: relative; - display: inline-block; - width: 1.8rem; - height: 1.55rem; - border: 2px solid var(--ink); - background: var(--paper); -} -.wordmark-icon i { position: absolute; width: 3px; background: var(--ink); bottom: 3px; } -.wordmark-icon i:nth-child(1) { left: 4px; height: 6px; } -.wordmark-icon i:nth-child(2) { left: 10px; height: 10px; } -.wordmark-icon i:nth-child(3) { left: 16px; height: 14px; background: var(--orange); } -.site-header nav { display: flex; align-items: center; gap: 1.8rem; font: 650 .77rem/1 var(--mono); } +.site-header { position: relative; z-index: 30; display: flex; width: var(--shell); min-height: 5.2rem; margin: 0 auto; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--ink); } +.wordmark { display: inline-flex; align-items: center; gap: .7rem; color: var(--ink); font: 750 1.05rem var(--display); letter-spacing: -.015em; text-decoration: none; } +.wordmark svg { width: 1.65rem; height: 1.65rem; } +.wordmark svg path:first-child { fill: var(--coral); stroke: none; } +.wordmark svg path:last-child { fill: none; stroke: white; stroke-width: 1.7; } +.site-header nav { display: flex; align-items: center; gap: 1.8rem; font: 650 .72rem var(--mono); } .site-header nav > a { text-decoration: none; } -.site-header nav > a:not(.nav-cta) { color: #4e5664; } -.site-header nav > a:not(.nav-cta):hover { color: var(--package); } -.nav-cta { - display: inline-flex; - align-items: center; - gap: .45rem; - padding: .7rem .8rem; - border: 1px solid var(--ink); - background: var(--ink); - color: white; -} -.nav-cta svg, .button svg { width: 1.05rem; fill: currentColor; } - -.hero { - display: grid; - grid-template-columns: minmax(0, 1.08fr) minmax(26rem, .92fr); - align-items: center; - gap: clamp(3rem, 7vw, 8rem); - width: var(--shell); - margin: 0 auto; - padding: clamp(5rem, 9vw, 9rem) 0 clamp(4rem, 7vw, 7rem); -} -.hero-copy { max-width: 48rem; } -.eyebrow { - display: flex; - align-items: center; - gap: .55rem; - margin: 0 0 1.25rem; - color: #505866; - font: 700 .72rem/1.3 var(--mono); - letter-spacing: .055em; - text-transform: uppercase; -} -.status-dot { width: .58rem; height: .58rem; border-radius: 50%; background: var(--proof); box-shadow: 0 0 0 3px #dfffb5; } -h1, h2, h3 { margin-top: 0; font-family: var(--display); letter-spacing: -.055em; } -h1 { - max-width: 10ch; - margin-bottom: 1.8rem; - font-size: clamp(4.3rem, 7.4vw, 8.5rem); - font-weight: 780; - line-height: .86; -} -h1 span { display: inline-block; color: var(--orange); } -.lede { max-width: 39rem; margin: 0; color: #454d5b; font-size: clamp(1.08rem, 1.5vw, 1.36rem); line-height: 1.5; } -.lede strong { color: var(--ink); font-weight: 700; } -.hero-actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 2.4rem; } -.button { - display: inline-flex; - align-items: center; - justify-content: center; - gap: .55rem; - min-height: 3.3rem; - padding: 0 1.15rem; - border: 1px solid var(--ink); - font: 700 .77rem/1 var(--mono); - text-decoration: none; - transition: transform 160ms ease, box-shadow 160ms ease, background 160ms ease; -} -.button:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--ink); } -.button-primary { background: var(--ink); color: white; } -.button-secondary { background: transparent; } -.hero-note { margin: 1.25rem 0 0; color: #6c7380; font: .68rem/1.5 var(--mono); } - -.xray { border: 1px solid var(--ink); background: var(--paper); box-shadow: 14px 14px 0 var(--ink); } -.xray-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 3.2rem; padding: .55rem .7rem; border-bottom: 1px solid var(--rule); background: #e9ecf2; } -.xray-tabs { display: flex; gap: .35rem; } -.xray-tab { - padding: .57rem .72rem; - border: 1px solid transparent; - background: transparent; - color: #565e6b; - font: 700 .66rem/1 var(--mono); - cursor: pointer; -} -.xray-tab.is-active { border-color: var(--ink); background: var(--paper); color: var(--ink); box-shadow: 2px 2px 0 var(--ink); } -.xray-file { color: #68717f; font: .65rem var(--mono); } -.xray-stage { min-height: 34rem; padding: 2rem; background-image: linear-gradient(#e3e6ec 1px, transparent 1px), linear-gradient(90deg, #e3e6ec 1px, transparent 1px); background-size: 18px 18px; } -.xray-view { animation: reveal 360ms cubic-bezier(.2,.8,.2,1); } -@keyframes reveal { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } } -.canvas-view { display: grid; grid-template-rows: 1fr auto; min-height: 30rem; } -.slide-art { - position: relative; - width: 100%; - aspect-ratio: 16 / 9; - overflow: hidden; - align-self: center; - padding: 8% 8%; - background: var(--ink); - color: white; - box-shadow: 0 10px 24px rgb(23 26 34 / .2); -} -.slide-art::after { content: ""; position: absolute; width: 9rem; height: 9rem; right: -3.5rem; top: -3.5rem; border: 1.7rem solid var(--orange); transform: rotate(14deg); } -.slide-kicker { color: #b9c2d4; font: 700 .52rem var(--mono); letter-spacing: .12em; } -.slide-title { margin-top: 6%; font: 760 clamp(1.35rem, 3vw, 2.3rem)/.92 var(--display); letter-spacing: -.04em; } -.slide-rule { width: 42%; height: 4px; margin-top: 7%; background: var(--orange); } -.slide-chart { position: absolute; display: flex; align-items: flex-end; gap: 4%; width: 33%; height: 35%; right: 8%; bottom: 14%; border-bottom: 1px solid #798296; } -.slide-chart i { width: 17%; height: var(--h); background: #f4f6fa; } -.slide-chart i:last-child { background: var(--orange); } -.slide-caption { position: absolute; right: 8%; bottom: 7%; color: #aab3c3; font: .48rem var(--mono); } -.xray-caption { margin: 1.2rem 0 0; color: #626a77; font: .72rem/1.4 var(--mono); } -.xray-caption b { color: var(--ink); } -.package-view { min-height: 30rem; background: var(--paper); border: 1px solid var(--ink); box-shadow: 8px 8px 0 var(--package); } -.package-head { display: flex; justify-content: space-between; gap: 1rem; padding: 1rem; border-bottom: 1px solid var(--ink); font: .7rem var(--mono); } -.package-head strong { color: var(--package); } -.package-tree { margin: 0; padding: 1rem 0; list-style: none; font: .72rem/1.4 var(--mono); } -.package-tree li { display: grid; grid-template-columns: 1rem 1fr auto; gap: .45rem; min-height: 2.35rem; align-items: center; padding: .2rem 1rem; border-bottom: 1px solid #e6e8ed; } -.package-tree em { color: #777f8c; font-style: normal; } -.package-tree .tree-depth-1 { padding-left: 2rem; } -.package-tree .tree-depth-2 { padding-left: 3rem; } -.package-tree .is-hot { background: var(--package-soft); color: #153a97; } -.package-tree .is-hot em { color: #4263ad; } -.tree-toggle, .file-icon { color: var(--package); } -.package-view .xray-caption { padding: 0 1rem 1rem; } -.xray-foot { display: flex; justify-content: space-between; gap: 1rem; padding: .75rem 1rem; border-top: 1px solid var(--rule); color: #626a77; font: .61rem var(--mono); } - -.proof-band { display: grid; grid-template-columns: 1.15fr repeat(4, 1fr); border-top: 1px solid var(--ink); border-bottom: 1px solid var(--ink); background: var(--paper); } -.proof-cell { display: flex; min-height: 10.5rem; flex-direction: column; justify-content: space-between; padding: 1.25rem; border-right: 1px solid var(--rule); text-decoration: none; } -.proof-cell:last-child { border-right: 0; } -.proof-label { color: #646c79; font: 700 .63rem var(--mono); letter-spacing: .05em; text-transform: uppercase; } -.proof-cell strong { font: 760 clamp(2.1rem, 3.7vw, 4rem)/1 var(--display); letter-spacing: -.05em; } -.proof-cell > span:last-child { color: #636b78; font: .68rem var(--mono); } -.proof-lead { background: var(--ink); color: white; } -.proof-lead .proof-label, .proof-lead > span:last-child { color: #bfc6d3; } -.proof-lead:hover { background: var(--package); } -.proof-warning { background: var(--orange-soft); } -.proof-warning strong { color: #a53920; } - -.section-shell { width: var(--shell); margin: 0 auto; } -.section-heading { max-width: 52rem; } -.section-heading h2, .method h2, .status h2, .reproduce h2 { - margin-bottom: 1.5rem; - font-size: clamp(3.3rem, 6.2vw, 7.2rem); - font-weight: 760; - line-height: .92; -} -.section-heading > p:last-child, .method-copy > p:not(.eyebrow):not(.acid-origin), .status-intro > p:last-child { max-width: 43rem; color: #59616e; font-size: 1.08rem; } - -.comparison { padding: clamp(7rem, 11vw, 11rem) 0 0; } -.comparison-intro { - display: grid; - grid-template-columns: 1.15fr .85fr; - gap: clamp(3rem, 8vw, 9rem); - align-items: end; -} -.comparison-intro h2 { - max-width: 13ch; - margin-bottom: 0; - font-size: clamp(3.3rem, 6.2vw, 7.2rem); - font-weight: 760; - line-height: .92; -} -.comparison-copy > p { margin: 0; color: #59616e; font-size: 1.08rem; } -.comparison-copy > div { display: flex; flex-wrap: wrap; gap: 1rem 1.6rem; margin-top: 1.5rem; } -.comparison-copy .text-link { margin-top: 0; } -.comparison-panel { - overflow-x: auto; - margin-top: 4rem; - border: 1px solid var(--ink); - background: var(--ink); - color: white; - box-shadow: 14px 14px 0 var(--package); -} -.comparison-toolbar, -.comparison-foot { - display: flex; - min-width: 52rem; - align-items: center; - justify-content: space-between; - gap: 1rem; - min-height: 3.2rem; - padding: 0 1rem; - color: #aeb6c5; - font: .64rem var(--mono); -} -.comparison-toolbar { border-bottom: 1px solid #464e5d; } -.comparison-foot { border-top: 1px solid #464e5d; } -.comparison-foot code { color: var(--proof); font: inherit; } -.comparison-dots { display: flex; gap: .35rem; } -.comparison-dots i { width: .62rem; height: .62rem; border: 1px solid #828b99; border-radius: 50%; } -.comparison-dots i:first-child { border-color: var(--orange); background: var(--orange); } -.comparison-chart { min-width: 52rem; } -.comparison-chart > p { margin: 0; padding: 2rem 1rem; color: #b9c1cd; font: .72rem var(--mono); } -.comparison-row { - display: grid; - grid-template-columns: minmax(13rem, 1.15fr) repeat(3, 1fr); - min-height: 7.5rem; - border-bottom: 1px solid #3b4350; -} -.comparison-row:last-child { border-bottom: 0; } -.comparison-row > * { display: flex; min-width: 0; padding: 1rem; border-right: 1px solid #3b4350; } -.comparison-row > *:last-child { border-right: 0; } -.comparison-row-head { - min-height: 2.8rem; - color: #939cab; - font: .59rem var(--mono); - letter-spacing: .05em; - text-transform: uppercase; -} -.comparison-row-head > * { align-items: center; } -.comparison-identity { flex-direction: column; justify-content: center; border-left: .28rem solid var(--package); } -.comparison-row-visual .comparison-identity { border-left-color: var(--orange); } -.comparison-identity strong { font: 750 1.2rem/1.05 var(--display); } -.comparison-identity span { margin-top: .45rem; color: #8f98a6; font: .61rem var(--mono); } -.comparison-metric { flex-direction: column; justify-content: center; } -.comparison-metric strong { font: 760 clamp(2rem, 3.1vw, 3.7rem)/1 var(--display); letter-spacing: -.045em; } -.comparison-metric sup { margin-left: .15rem; color: #8e98a8; font: 600 .72rem var(--mono); vertical-align: super; } -.comparison-track { display: block; width: 100%; height: .35rem; margin-top: .8rem; background: #3d4552; } -.comparison-track i { display: block; width: calc(var(--score) * 1%); height: 100%; background: var(--package); } -.comparison-row-visual .comparison-track i { background: var(--orange); } - -.launch-film { padding: clamp(7rem, 12vw, 13rem) 0; } -.film-frame { width: min(68rem, 100%); margin: 4rem auto 0; padding: 1rem; border: 1px solid var(--ink); background: var(--paper); box-shadow: 14px 14px 0 var(--orange); } -.film-frame video { display: block; width: 100%; aspect-ratio: 1; background: var(--ink); } -.film-meta { display: flex; flex-wrap: wrap; gap: 1.3rem; padding: 1rem .25rem 0; color: #656d7a; font: .65rem var(--mono); } -.film-meta a { margin-left: auto; color: var(--package); } - -.method { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(4rem, 9vw, 10rem); align-items: start; padding: 2rem 0 clamp(8rem, 13vw, 14rem); } -.method h2 { font-size: clamp(3.2rem, 5.4vw, 6rem); } -.acid-origin { max-width: 36rem; margin: 2rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--rule); color: var(--ink); font: 650 1.15rem/1.35 var(--display); } -.acid-origin a { color: var(--package); text-decoration-thickness: 1px; text-underline-offset: .2em; } -.text-link { display: inline-flex; gap: .5rem; margin-top: 1rem; color: var(--package); font: 700 .75rem var(--mono); text-underline-offset: .25rem; } -.method-list { padding-top: .8rem; } -.method-row { display: grid; grid-template-columns: 10rem 1fr 2.6rem; gap: 1rem; align-items: center; min-height: 3.5rem; border-bottom: 1px solid var(--rule); font: .72rem var(--mono); } -.method-row i { height: .75rem; width: calc(var(--value) / 108 * 100%); background: var(--package); } -.method-row b { text-align: right; } -.method-row-visual i { background: var(--orange); } -.method-divider { display: flex; justify-content: space-between; gap: 1rem; padding: 1.6rem 0 .6rem; color: var(--package); font: 750 .65rem var(--mono); text-transform: uppercase; } -.method-divider em { color: #747c89; font-style: normal; text-transform: none; } -.method-list > p { color: #777f8b; font: .63rem/1.5 var(--mono); } - -.collaborate { border-top: 1px solid var(--ink); border-bottom: 1px solid var(--ink); background: var(--ink); color: white; } -.collaborate-inner { padding: clamp(7rem, 11vw, 12rem) 0; } -.collaborate .eyebrow { color: var(--proof); } -.collaborate .section-heading > p:last-child { color: #b8c0ce; } -.contribution-grid { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 4rem; border-top: 1px solid #565e6c; border-left: 1px solid #565e6c; } -.contribution-card { display: flex; min-height: 25rem; flex-direction: column; padding: 1.5rem; border-right: 1px solid #565e6c; border-bottom: 1px solid #565e6c; text-decoration: none; transition: background 150ms ease, color 150ms ease; } -.contribution-card:hover { background: var(--proof); color: var(--ink); } -.card-index { color: #9ba4b2; font: .67rem var(--mono); } -.contribution-card:hover .card-index { color: #40501f; } -.contribution-card h3 { max-width: 10ch; margin: 3.5rem 0 1.2rem; font-size: 2.3rem; line-height: .98; } -.contribution-card p { color: #afb7c4; } -.contribution-card:hover p { color: #36411e; } -.card-action { margin-top: auto; color: var(--proof); font: 700 .69rem var(--mono); } -.contribution-card:hover .card-action { color: var(--ink); } -.repo-paths { display: grid; grid-template-columns: repeat(4, 1fr); margin-top: 2rem; gap: .75rem; } -.repo-paths a { padding: .85rem; border: 1px solid #4b5360; color: #dce1ea; font: .62rem var(--mono); text-decoration: none; } -.repo-paths a:hover { border-color: var(--proof); color: var(--proof); } -.repo-paths span { color: #777f8d; } - -.status { display: grid; grid-template-columns: .82fr 1.18fr; gap: clamp(4rem, 9vw, 10rem); padding: clamp(8rem, 13vw, 14rem) 0; } -.status h2 { font-size: clamp(3.2rem, 5.4vw, 6.2rem); } -.gate-list { border-top: 1px solid var(--ink); } -.gate { display: grid; grid-template-columns: 2rem 1fr auto; gap: 1rem; align-items: start; padding: 1.35rem 0; border-bottom: 1px solid var(--rule); } -.gate-icon { display: grid; width: 1.45rem; height: 1.45rem; place-items: center; border: 1px solid var(--ink); font: 700 .7rem var(--mono); } -.gate strong { font-size: 1rem; } -.gate p { margin: .25rem 0 0; color: #69717e; font-size: .88rem; } -.gate em { padding: .26rem .45rem; border: 1px solid currentColor; font: 700 .6rem var(--mono); font-style: normal; text-transform: uppercase; } -.gate-pass .gate-icon, .gate-pass em { color: #38601d; background: #ddf7bd; } -.gate-open .gate-icon, .gate-open em { color: #99412b; background: var(--orange-soft); } - -.reproduce { display: grid; grid-template-columns: .75fr 1.25fr; gap: clamp(3rem, 9vw, 10rem); align-items: start; padding-bottom: clamp(8rem, 13vw, 14rem); } -.reproduce h2 { font-size: clamp(3.2rem, 5vw, 5.8rem); } -.command-block { min-width: 0; border: 1px solid var(--ink); background: var(--ink); color: white; box-shadow: 10px 10px 0 var(--package); } -.command-head { display: flex; justify-content: space-between; align-items: center; min-height: 2.9rem; padding: 0 .9rem; border-bottom: 1px solid #424957; color: #aeb6c5; font: .65rem var(--mono); } -.command-head button { border: 1px solid #656d7a; padding: .35rem .55rem; background: transparent; color: white; font: .62rem var(--mono); cursor: pointer; } -.command-block pre { overflow-x: auto; margin: 0; padding: 1.4rem; color: #e7ebf2; font: .73rem/1.85 var(--mono); } -.command-block pre code::first-line { color: var(--proof); } -.command-block > p { min-height: 1.2rem; margin: 0; padding: 0 1.4rem 1rem; color: var(--proof); font: .61rem var(--mono); } - -.final-cta { display: grid; grid-template-columns: auto 1fr auto; gap: 2rem; align-items: center; padding: clamp(4rem, 7vw, 7rem) max(1.5rem, calc((100vw - min(92rem, calc(100vw - 3rem))) / 2)); border-top: 1px solid var(--ink); background: var(--orange); color: white; } -.final-cta .eyebrow { color: #ffe3da; } -.final-cta h2 { max-width: 14ch; margin: 0; font-size: clamp(2.4rem, 5vw, 5rem); line-height: .95; } -.final-cta .button-primary { background: white; color: var(--ink); } -.final-mark { position: relative; display: grid; width: clamp(5rem, 9vw, 8rem); aspect-ratio: 1; place-items: center; border: 2px solid white; font: 800 clamp(2.5rem, 5vw, 4.5rem) var(--display); } -.final-mark i { position: absolute; right: 10%; bottom: 10%; width: 6%; background: white; } -.final-mark i:nth-of-type(1) { height: 18%; right: 25%; } -.final-mark i:nth-of-type(2) { height: 28%; right: 15%; } -.final-mark i:nth-of-type(3) { height: 40%; right: 5%; background: var(--ink); } - -.site-footer { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 2rem; width: var(--shell); min-height: 6.5rem; margin: 0 auto; color: #656d79; font: .62rem var(--mono); } -.site-footer p { margin: 0; text-align: center; text-transform: uppercase; letter-spacing: .04em; } +.site-header nav > a:not(.github-link):hover { text-decoration: underline; text-underline-offset: .35rem; } +.github-link { display: inline-flex; min-height: 2.55rem; align-items: center; gap: .55rem; padding: 0 .9rem; background: var(--ink); color: white; } +.github-link svg { width: 1rem; fill: currentColor; } + +.hero { display: grid; grid-template-columns: .82fr 1.18fr; gap: clamp(3rem, 7vw, 8rem); width: var(--shell); min-height: calc(100vh - 5.2rem); margin: 0 auto; padding: clamp(5rem, 9vw, 9rem) 0 clamp(6rem, 10vw, 10rem); align-items: center; } +.hero-copy { max-width: 42rem; } +.overline { margin: 0 0 1.2rem; color: var(--coral); font: 750 .67rem var(--mono); letter-spacing: .055em; text-transform: uppercase; } +h1, h2, h3 { font-family: var(--display); letter-spacing: -.045em; } +h1 { max-width: 9ch; margin: 0; font-size: clamp(4.3rem, 7.1vw, 8rem); font-weight: 800; line-height: .84; } +h1 em { color: var(--coral); font-style: normal; } +.hero-lede { max-width: 39rem; margin: 2rem 0 0; color: #424b57; font-size: clamp(1.08rem, 1.45vw, 1.35rem); line-height: 1.48; } +.hero-actions, .prompt-actions { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; margin-top: 2.1rem; } +.button { display: inline-flex; min-height: 3.45rem; align-items: center; justify-content: center; gap: 1rem; padding: 0 1.25rem; border: 1px solid var(--ink); font: 750 .72rem var(--mono); text-decoration: none; transition: transform 120ms ease, box-shadow 120ms ease; } +.button:hover { transform: translate(-3px, -3px); box-shadow: 4px 4px 0 var(--ink); } +.button.primary { background: var(--ink); color: white; } +.button.secondary { background: transparent; } +.button.light { border-color: white; background: white; color: var(--ink); } +.acid-line { max-width: 38rem; margin: 2.2rem 0 0; padding-top: 1.1rem; border-top: 1px solid var(--rule); color: #48515d; font-size: .86rem; } +.acid-line a { color: var(--ink); text-underline-offset: .2rem; } + +.editor { min-width: 0; border: 1px solid var(--ink); background: var(--paper); box-shadow: 16px 16px 0 var(--ink); } +.editor-bar { display: grid; grid-template-columns: 1fr auto 1fr; min-height: 2.9rem; align-items: center; gap: 1rem; padding: 0 .8rem; border-bottom: 1px solid #424b57; background: var(--ink); color: #c3c9d1; font: .62rem var(--mono); } +.editor-bar > span:nth-child(2) { color: white; } +.editor-bar > span:last-child { justify-self: end; } +.window-dots { display: flex; gap: .35rem; } +.window-dots i { display: block; width: .58rem; height: .58rem; border: 1px solid #7c8591; border-radius: 50%; } +.window-dots i:first-child { border-color: var(--coral); background: var(--coral); } +.editor-body { display: grid; grid-template-columns: 1fr 11rem; min-height: 31rem; } +.editor-canvas { display: grid; place-items: center; min-width: 0; padding: clamp(1rem, 2.5vw, 2.5rem); background: #cfd3da; } +.slide-wrap { position: relative; width: 100%; aspect-ratio: 16 / 9; background: var(--ink); box-shadow: 0 .8rem 2rem rgb(31 41 53 / 18%); } +.slide-wrap > img { display: block; width: 100%; height: 100%; object-fit: cover; } +.selection { --x: 39.2%; --y: 6.2%; --w: 22%; --h: 12%; position: absolute; left: var(--x); top: var(--y); width: var(--w); height: var(--h); border: 2px solid var(--blue); pointer-events: none; transition: left 260ms ease, top 260ms ease, width 260ms ease, height 260ms ease; } +.selection i { position: absolute; width: .55rem; height: .55rem; border: 1px solid var(--blue); background: white; } +.selection i:nth-child(1) { left: -.35rem; top: -.35rem; } +.selection i:nth-child(2) { right: -.35rem; top: -.35rem; } +.selection i:nth-child(3) { left: -.35rem; bottom: -.35rem; } +.selection i:nth-child(4) { right: -.35rem; bottom: -.35rem; } +.editor[data-inspector="art"] .selection { --x: 41%; --y: 5%; --w: 54%; --h: 70%; } +.editor[data-inspector="footer"] .selection { --x: 0%; --y: 92%; --w: 100%; --h: 8%; } +.object-panel { display: flex; min-width: 0; flex-direction: column; padding: 1.1rem; border-left: 1px solid var(--rule); background: #f8f9fb; } +.panel-label { color: #737c88; font: .55rem var(--mono); text-transform: uppercase; } +.object-panel > strong { margin-top: 1.2rem; font: 750 1.2rem/1.05 var(--display); } +.object-panel > p { margin: .55rem 0 0; color: #5d6672; font-size: .7rem; line-height: 1.45; } +.object-panel dl { margin: 1.4rem 0 0; border-top: 1px solid var(--rule); } +.object-panel dl div { display: flex; justify-content: space-between; gap: .6rem; padding: .55rem 0; border-bottom: 1px solid var(--rule); font: .55rem var(--mono); } +.object-panel dt { color: #737c88; } +.object-panel dd { margin: 0; color: var(--ink); } +.object-tabs { display: grid; gap: .35rem; margin-top: auto; } +.object-tabs button { min-height: 2.1rem; border: 1px solid var(--rule); background: white; color: var(--ink); font: .58rem var(--mono); text-align: left; cursor: pointer; } +.object-tabs button.is-active { border-color: var(--blue); color: var(--blue); box-shadow: inset 3px 0 0 var(--blue); } + +.statement { display: grid; grid-template-columns: .8fr 1.2fr; gap: 3rem; align-items: baseline; padding: 3.8rem max(1.5rem, calc((100vw - min(94rem, calc(100vw - 3rem))) / 2)); background: var(--coral); color: white; } +.statement p { margin: 0; font: .72rem var(--mono); text-transform: uppercase; } +.statement strong { font: 750 clamp(2.1rem, 4vw, 4.6rem)/.98 var(--display); letter-spacing: -.035em; } + +.quickstart { width: var(--shell); margin: 0 auto; padding: clamp(7rem, 12vw, 13rem) 0; } +.section-intro { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(3rem, 10vw, 11rem); align-items: end; } +.section-intro .overline { grid-column: 1 / -1; margin-bottom: -1.5rem; } +h2 { margin: 0; font-size: clamp(3.5rem, 6.3vw, 7rem); font-weight: 780; line-height: .9; } +.section-intro > p:last-child, .deck-head > p, .prompt-copy > p:not(.overline), .format-copy p, .future p, .contribute p { margin: 0; color: #4e5763; font-size: 1.05rem; } +.steps { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 4rem; border-top: 1px solid var(--ink); border-left: 1px solid var(--ink); } +.steps article { display: flex; min-height: 17rem; flex-direction: column; padding: 1.3rem; border-right: 1px solid var(--ink); border-bottom: 1px solid var(--ink); background: var(--paper); } +.steps article > span { font: .62rem var(--mono); } +.steps h3 { margin: 2.7rem 0 .6rem; font-size: 2.1rem; line-height: 1; } +.steps p { margin: 0; color: #5d6672; font-size: .9rem; } +.steps a, .steps small, .steps code { margin-top: auto; color: var(--ink); font: .63rem var(--mono); } +.steps code { overflow-x: auto; display: block; padding: .7rem; background: var(--canvas); white-space: nowrap; } +.terminal { margin: 2rem 0 0 auto; width: min(57rem, 100%); border: 1px solid var(--ink); background: var(--ink); color: #e9edf2; box-shadow: 10px 10px 0 var(--teal); } +.terminal-head { display: flex; min-height: 2.8rem; align-items: center; justify-content: space-between; padding: 0 .9rem; border-bottom: 1px solid #45505d; color: #adb5c0; font: .61rem var(--mono); } +.terminal-head button { border: 1px solid #65707d; padding: .3rem .55rem; background: transparent; color: white; font: .58rem var(--mono); cursor: pointer; } +.terminal pre { overflow-x: auto; margin: 0; padding: 1.2rem 1.4rem; border-bottom: 1px solid #45505d; color: white; font: .76rem/1.6 var(--mono); } +.terminal-output { padding: 1.2rem 1.4rem; color: #cfd5dd; font: .68rem/1.85 var(--mono); } +.terminal-output span { color: #aee97e; } +.terminal > p { min-height: 1rem; margin: 0; padding: 0 1.4rem .8rem; color: #aee97e; font: .58rem var(--mono); } + +.deck { padding: clamp(7rem, 11vw, 12rem) 0 0; border-top: 1px solid var(--ink); background: #f8f9fb; } +.deck-head { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(3rem, 9vw, 10rem); width: var(--shell); margin: 0 auto; align-items: end; } +.deck-head h2 { max-width: 13ch; font-size: clamp(3.4rem, 5.7vw, 6.4rem); } +.slide-index { position: sticky; z-index: 20; top: 0; display: grid; grid-template-columns: 11rem 1fr; width: var(--shell); min-height: 4rem; margin: 5rem auto 0; align-items: center; border: 1px solid var(--ink); background: var(--ink); color: white; } +.slide-index > span { padding: 0 1rem; color: #aeb6c1; font: .63rem var(--mono); } +.slide-index > span b { color: white; } +.slide-index > div { display: flex; overflow-x: auto; height: 100%; border-left: 1px solid #4c5663; } +.slide-index a { display: grid; flex: 0 0 2.55rem; place-items: center; color: #8d97a4; font: .57rem var(--mono); text-decoration: none; } +.slide-index a:hover, .slide-index a.is-active { background: var(--coral); color: white; } +.slide-list { margin-top: 3rem; } +.slide-case { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(18rem, .5fr); gap: clamp(2rem, 6vw, 7rem); width: var(--shell); margin: 0 auto; padding: clamp(4rem, 8vw, 8rem) 0; border-top: 1px solid var(--rule); align-items: start; } +.slide-case:nth-child(even) { grid-template-columns: minmax(18rem, .5fr) minmax(0, 1.5fr); } +.slide-case:nth-child(even) .slide-image { grid-column: 2; } +.slide-case:nth-child(even) .slide-copy { grid-column: 1; grid-row: 1; } +.slide-image { display: block; border: 1px solid #707885; background: var(--ink); box-shadow: 12px 12px 0 var(--canvas-deep); transition: transform 160ms ease, box-shadow 160ms ease; } +.slide-image:hover { transform: translate(-3px, -3px); box-shadow: 16px 16px 0 var(--teal); } +.slide-image img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; } +.slide-copy { position: sticky; top: 7rem; padding-top: .25rem; } +.slide-number { color: var(--coral); font: .62rem var(--mono); text-transform: uppercase; } +.slide-copy h3 { margin: 1.4rem 0 1.2rem; font-size: clamp(2.2rem, 3.4vw, 4rem); line-height: .92; } +.slide-copy > p { margin: 0; color: #505965; } +.slide-copy h4 { margin: 2rem 0 .5rem; font: 750 .61rem var(--mono); letter-spacing: .045em; text-transform: uppercase; } +.slide-copy a { display: inline-block; margin-top: 2rem; color: var(--ink); font: 700 .65rem var(--mono); text-underline-offset: .3rem; } + +.prompt { display: grid; grid-template-columns: .85fr 1.15fr; gap: clamp(3rem, 9vw, 10rem); align-items: center; padding: clamp(8rem, 13vw, 14rem) max(1.5rem, calc((100vw - min(94rem, calc(100vw - 3rem))) / 2)); background: var(--teal); } +.prompt-copy h2 { max-width: 10ch; } +.prompt-copy > p:not(.overline) { max-width: 39rem; margin-top: 2rem; color: #15383c; } +.prompt .overline { color: #15383c; } +.prompt-actions .text-link { font: 700 .65rem var(--mono); text-underline-offset: .3rem; } +.prompt-file { min-width: 0; border: 1px solid var(--ink); background: var(--ink); color: #e8edf3; box-shadow: 14px 14px 0 #13737c; } +.file-head { display: flex; min-height: 3rem; align-items: center; justify-content: space-between; padding: 0 1rem; border-bottom: 1px solid #46515e; color: #aab3be; font: .61rem var(--mono); } +.prompt-file pre { overflow-x: auto; margin: 0; padding: clamp(1.4rem, 3vw, 2.5rem); font: clamp(.65rem, 1vw, .78rem)/1.9 var(--mono); } + +.formats { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(3rem, 10vw, 11rem); width: var(--shell); margin: 0 auto; padding: clamp(8rem, 13vw, 14rem) 0; } +.formats h2 { max-width: 10ch; font-size: clamp(3.7rem, 6vw, 6.8rem); } +.format-copy { padding-top: 2rem; border-top: 1px solid var(--ink); } +.format-copy p + p { margin-top: 1.5rem; } +.format-copy a { display: inline-block; margin-top: 1.8rem; font: 700 .68rem var(--mono); text-underline-offset: .3rem; } + +.future { display: grid; grid-template-columns: .65fr 1.35fr; gap: clamp(3rem, 10vw, 11rem); align-items: center; padding: clamp(7rem, 11vw, 11rem) max(1.5rem, calc((100vw - min(94rem, calc(100vw - 3rem))) / 2)); background: var(--gold); } +.future-mark { font: 800 clamp(5rem, 13vw, 14rem)/.8 var(--display); letter-spacing: -.08em; white-space: nowrap; } +.future-mark span { color: white; } +.future h2 { max-width: 12ch; font-size: clamp(3.4rem, 5.5vw, 6rem); } +.future p:last-child { max-width: 49rem; margin-top: 1.8rem; color: #40381d; } +.future .overline { color: #5d4c0f; } + +.contribute { display: grid; grid-template-columns: .8fr 1.2fr; gap: clamp(3rem, 9vw, 10rem); align-items: start; padding: clamp(8rem, 13vw, 14rem) max(1.5rem, calc((100vw - min(94rem, calc(100vw - 3rem))) / 2)); background: var(--ink); color: white; } +.contribute h2 { max-width: 10ch; font-size: clamp(3.7rem, 6vw, 6.8rem); } +.contribute p:last-child { max-width: 35rem; margin-top: 1.7rem; color: #aeb7c3; } +.contribution-links { border-top: 1px solid #505a67; } +.contribution-links a { display: grid; grid-template-columns: 3rem 1fr auto; gap: 1rem; align-items: center; min-height: 8rem; border-bottom: 1px solid #505a67; color: white; text-decoration: none; transition: background 140ms ease, color 140ms ease; } +.contribution-links a:hover { padding: 0 1rem; background: var(--coral); } +.contribution-links span, .contribution-links em { color: #858f9c; font: .6rem var(--mono); font-style: normal; } +.contribution-links a:hover span, .contribution-links a:hover em { color: white; } +.contribution-links strong { font: 750 clamp(1.5rem, 2.3vw, 2.5rem) var(--display); } + +.final-cta { display: flex; min-height: 26rem; align-items: center; justify-content: space-between; gap: 3rem; padding: 4rem max(1.5rem, calc((100vw - min(94rem, calc(100vw - 3rem))) / 2)); background: var(--coral); color: white; } +.final-cta .overline { color: #ffe4de; } +.final-cta h2 { font-size: clamp(3.5rem, 7vw, 7.5rem); } + +.site-footer { display: grid; grid-template-columns: auto 1fr auto; gap: 2rem; align-items: center; width: var(--shell); min-height: 7rem; margin: 0 auto; color: #626b77; font: .6rem var(--mono); } +.site-footer p { margin: 0; text-align: center; text-transform: uppercase; } .site-footer > div { display: flex; gap: 1rem; } -.site-footer > div a { text-underline-offset: .2rem; } +.site-footer a { text-underline-offset: .25rem; } -@media (max-width: 1000px) { - .site-header nav > a:not(.nav-cta) { display: none; } +@media (max-width: 1100px) { .hero { grid-template-columns: 1fr; } .hero-copy { max-width: 52rem; } - .xray { width: min(42rem, calc(100% - 1rem)); margin: 0 auto; } - .proof-band { grid-template-columns: repeat(2, 1fr); } - .proof-cell { border-bottom: 1px solid var(--rule); } - .proof-lead { grid-column: 1 / -1; } - .comparison-intro, .method, .status, .reproduce { grid-template-columns: 1fr; } - .method-copy, .status-intro, .reproduce > div:first-child { max-width: 48rem; } - .contribution-grid { grid-template-columns: 1fr; } - .contribution-card { min-height: 18rem; } - .repo-paths { grid-template-columns: repeat(2, 1fr); } + .editor { width: min(58rem, calc(100% - 1rem)); margin: 0 auto; } + .section-intro, .deck-head, .prompt, .formats, .future, .contribute { grid-template-columns: 1fr; } + .section-intro .overline { margin-bottom: 0; } + .section-intro > p:last-child, .deck-head > p { max-width: 48rem; } + .prompt-file { width: min(58rem, 100%); } + .future-mark { font-size: clamp(5rem, 22vw, 13rem); } + .contribute > div:first-child { max-width: 52rem; } } -@media (max-width: 640px) { +@media (max-width: 800px) { :root { --shell: calc(100vw - 2rem); } - body { font-size: 16px; } - .site-header { min-height: 4.4rem; } - .nav-cta span { display: none; } - .hero { padding-top: 4rem; } - h1 { font-size: clamp(3.8rem, 18vw, 5.5rem); } - .hero-actions .button { width: 100%; } - .xray { width: calc(100% - .6rem); box-shadow: 8px 8px 0 var(--ink); } - .xray-stage { min-height: 28rem; padding: .8rem; } - .canvas-view, .package-view { min-height: 26rem; } - .xray-file { display: none; } - .package-tree { font-size: .62rem; } - .package-tree em { display: none; } - .package-tree li { grid-template-columns: 1rem 1fr; } - .proof-band { grid-template-columns: 1fr; } - .proof-lead { grid-column: auto; } - .proof-cell { min-height: 8.5rem; } - .section-heading h2, .method h2, .status h2, .reproduce h2 { font-size: clamp(3rem, 15vw, 4.5rem); } - .comparison-panel { box-shadow: 8px 8px 0 var(--package); } - .film-frame { padding: .5rem; box-shadow: 8px 8px 0 var(--orange); } - .film-meta a { width: 100%; margin: 0; } - .method-row { grid-template-columns: 8rem 1fr 2rem; gap: .55rem; font-size: .62rem; } - .repo-paths { grid-template-columns: 1fr; } - .gate { grid-template-columns: 1.6rem 1fr; } - .gate > em { grid-column: 2; justify-self: start; } - .command-block { box-shadow: 7px 7px 0 var(--package); } - .final-cta { grid-template-columns: 1fr; } - .final-mark { width: 5rem; } - .final-cta .button { width: 100%; } - .site-footer { grid-template-columns: 1fr; gap: 1rem; padding: 2rem 0; } + body::before { width: .25rem; } + .site-header { min-height: 4.5rem; } + .site-header nav > a:not(.github-link) { display: none; } + .hero { min-height: auto; padding-top: 4rem; } + h1 { font-size: clamp(4rem, 19vw, 6.5rem); } + .editor { width: calc(100% - .5rem); box-shadow: 8px 8px 0 var(--ink); } + .editor-body { grid-template-columns: 1fr; min-height: 0; } + .editor-canvas { padding: 1rem; } + .object-panel { min-height: 12rem; border-top: 1px solid var(--rule); border-left: 0; } + .object-tabs { grid-template-columns: repeat(3, 1fr); margin-top: 1rem; } + .statement { grid-template-columns: 1fr; gap: 1rem; } + .steps { grid-template-columns: 1fr; } + .steps article { min-height: 13rem; } + .slide-index { grid-template-columns: 7rem 1fr; } + .slide-case, .slide-case:nth-child(even) { grid-template-columns: 1fr; gap: 2rem; } + .slide-case:nth-child(even) .slide-image, .slide-case:nth-child(even) .slide-copy { grid-column: 1; grid-row: auto; } + .slide-copy { position: static; max-width: 42rem; } + .prompt, .future, .contribute { padding-right: 1rem; padding-left: 1rem; } + .final-cta { align-items: flex-start; flex-direction: column; justify-content: center; } + .site-footer { grid-template-columns: 1fr; gap: .8rem; padding: 2rem 0; } .site-footer p { text-align: left; } } +@media (max-width: 520px) { + .hero-actions .button, .prompt-actions .button, .final-cta .button { width: 100%; } + .editor-bar { grid-template-columns: 1fr auto; } + .editor-bar .window-dots { display: none; } + .terminal { box-shadow: 6px 6px 0 var(--teal); } + .slide-index > span { padding: 0 .6rem; } + .slide-case { padding: 3.5rem 0; } + .slide-image { box-shadow: 7px 7px 0 var(--canvas-deep); } + .contribution-links a { grid-template-columns: 2rem 1fr; } + .contribution-links em { grid-column: 2; margin-top: -1.5rem; } +} + @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } + *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; } } diff --git a/site/site.js b/site/site.js index 1a331d6..10124ba 100644 --- a/site/site.js +++ b/site/site.js @@ -1,165 +1,90 @@ -const xray = document.querySelector(".xray"); -const xrayButtons = [...document.querySelectorAll("[data-xray-target]")]; -const xrayPanels = [...document.querySelectorAll("[data-xray-panel]")]; -let xrayTimer; +const inspector = document.querySelector("[data-inspector]"); +const inspectorButtons = [...document.querySelectorAll("[data-inspect]")]; +const objectName = document.querySelector("[data-object-name]"); +const objectDetail = document.querySelector("[data-object-detail]"); +const objectType = document.querySelector("[data-object-type]"); + +const inspectorContent = { + title: { + name: "Title placeholder", + detail: "Editable text · inherited from the Title Slide layout", + type: "Placeholder", + }, + art: { + name: "Image + three shapes", + detail: "Independent objects · crop · z-order · transparency · shadow", + type: "Picture / shapes", + }, + footer: { + name: "Master footer", + detail: "Slide number field · rule · repeated GLOSS label", + type: "Master objects", + }, +}; -function showXrayLayer(layer, { userInitiated = false } = {}) { - if (!xray) return; - xray.dataset.xray = layer; - for (const button of xrayButtons) { - const active = button.dataset.xrayTarget === layer; +function inspectObject(name) { + const content = inspectorContent[name]; + if (!inspector || !content) return; + inspector.dataset.inspector = name; + if (objectName) objectName.textContent = content.name; + if (objectDetail) objectDetail.textContent = content.detail; + if (objectType) objectType.textContent = content.type; + for (const button of inspectorButtons) { + const active = button.dataset.inspect === name; button.classList.toggle("is-active", active); button.setAttribute("aria-pressed", String(active)); } - for (const panel of xrayPanels) { - panel.hidden = panel.dataset.xrayPanel !== layer; - } - if (userInitiated) window.clearTimeout(xrayTimer); -} - -for (const button of xrayButtons) { - button.addEventListener("click", () => showXrayLayer(button.dataset.xrayTarget, { userInitiated: true })); -} - -const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; -if (!reducedMotion) { - xrayTimer = window.setTimeout(() => showXrayLayer("package"), 1500); } -const evidenceBindings = { - "candidate-checks": (data) => data.candidate_checks.total, - "visual-checks": (data) => data.candidate_checks.visual_render, - "inside-file-checks": (data) => data.candidate_checks.inside_file, - "inside-file-percent": (data) => data.candidate_checks.inside_file_percent, - "positive-passed": (data) => data.operator_evidence.positive_controls_passed, - "positive-total": (data) => data.operator_evidence.positive_controls, - "mutants-detected": (data) => data.operator_evidence.single_fault_mutations_detected, - "mutants-total": (data) => data.operator_evidence.single_fault_mutations, -}; - -fetch("/evidence/preview-v1.json") - .then((response) => { - if (!response.ok) throw new Error(`Evidence request failed: ${response.status}`); - return response.json(); - }) - .then((data) => { - document.documentElement.dataset.evidenceId = data.evidence_id; - for (const [key, readValue] of Object.entries(evidenceBindings)) { - for (const node of document.querySelectorAll(`[data-evidence="${key}"]`)) { - node.textContent = String(readValue(data)); - } - } - }) - .catch(() => { - document.documentElement.dataset.evidenceId = "fallback-copy"; - }); - -const comparativeChart = document.querySelector("[data-comparative-chart]"); -const comparativeMetrics = [ - ["local_fidelity_percent", "Local fidelity"], - ["mean_visual_ssim_percent", "Visual SSIM"], - ["native_weighted_pass_percent", "Native pass"], -]; - -function comparativeCell(value, label) { - const cell = document.createElement("div"); - cell.className = "comparison-metric"; - cell.setAttribute("aria-label", `${label}: ${value.toFixed(2)} percent`); - - const number = document.createElement("strong"); - number.textContent = value.toFixed(2); - const percent = document.createElement("sup"); - percent.textContent = "%"; - number.append(percent); - - const track = document.createElement("span"); - track.className = "comparison-track"; - const bar = document.createElement("i"); - bar.style.setProperty("--score", String(value)); - track.append(bar); - cell.append(number, track); - return cell; -} - -function renderComparative(data) { - if (!comparativeChart) return; - comparativeChart.replaceChildren(); - - const header = document.createElement("div"); - header.className = "comparison-row comparison-row-head"; - const pathHeading = document.createElement("span"); - pathHeading.textContent = "Generation path"; - header.append(pathHeading); - for (const [, label] of comparativeMetrics) { - const metricHeading = document.createElement("span"); - metricHeading.textContent = label; - header.append(metricHeading); - } - comparativeChart.append(header); - - for (const path of data.paths) { - const row = document.createElement("div"); - row.className = `comparison-row comparison-row-${path.path_id.split("-")[0]}`; - - const identity = document.createElement("div"); - identity.className = "comparison-identity"; - const label = document.createElement("strong"); - label.textContent = path.label; - const runCount = document.createElement("span"); - runCount.textContent = `${path.runs.length} seeded runs`; - identity.append(label, runCount); - row.append(identity); - - for (const [key, metricLabel] of comparativeMetrics) { - row.append(comparativeCell(path.mean_metrics[key], metricLabel)); - } - comparativeChart.append(row); - } - - for (const node of document.querySelectorAll("[data-comparative-label]")) { - node.textContent = data.disclosure.verification_label; - } - const cohort = data.cohort.scoring_cohort_id.replace("sha256:", ""); - for (const node of document.querySelectorAll("[data-cohort-short]")) { - node.textContent = cohort.slice(0, 12); - node.setAttribute("title", data.cohort.scoring_cohort_id); - } - for (const node of document.querySelectorAll("[data-comparative-runs]")) { - node.textContent = String(data.totals.runs); - } - for (const node of document.querySelectorAll("[data-comparative-slides]")) { - node.textContent = String(data.totals.slides); - } +for (const button of inspectorButtons) { + button.addEventListener("click", () => inspectObject(button.dataset.inspect)); } -fetch("/evidence/comparative-v1-summary.json") - .then((response) => { - if (!response.ok) throw new Error(`Comparative summary request failed: ${response.status}`); - return response.json(); - }) - .then(renderComparative) - .catch(() => { - if (comparativeChart) { - comparativeChart.textContent = "Comparative summary unavailable. Open the public JSON evidence."; - } - }); - -const copyButton = document.querySelector("[data-copy-command]"); +const copyButton = document.querySelector("[data-copy-check]"); const copyStatus = document.querySelector("[data-copy-status]"); -const command = document.querySelector(".command-block code"); +const checkCommand = "uv run gloss check edited.pptx"; -if (copyButton && command) { +if (copyButton) { copyButton.addEventListener("click", async () => { try { - await navigator.clipboard.writeText(command.textContent.trim()); + await navigator.clipboard.writeText(checkCommand); copyButton.textContent = "Copied"; - if (copyStatus) copyStatus.textContent = "Commands copied to clipboard."; + if (copyStatus) copyStatus.textContent = "Command copied to clipboard."; window.setTimeout(() => { - copyButton.textContent = "Copy"; + copyButton.textContent = "Copy command"; if (copyStatus) copyStatus.textContent = ""; }, 1800); } catch { - if (copyStatus) copyStatus.textContent = "Select the commands and copy them manually."; + if (copyStatus) copyStatus.textContent = "Select the command and copy it manually."; } }); } + +const slideCases = [...document.querySelectorAll("[data-slide]")]; +const slideLinks = [...document.querySelectorAll("[data-slide-link]")]; +const currentSlide = document.querySelector("[data-current-slide]"); + +function setCurrentSlide(number) { + if (currentSlide) currentSlide.textContent = number; + for (const link of slideLinks) { + const active = link.dataset.slideLink === number; + link.classList.toggle("is-active", active); + if (active) link.setAttribute("aria-current", "true"); + else link.removeAttribute("aria-current"); + } +} + +if ("IntersectionObserver" in window) { + const ratios = new Map(); + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + ratios.set(entry.target, entry.isIntersecting ? entry.intersectionRatio : 0); + } + const visible = [...ratios.entries()].sort((a, b) => b[1] - a[1])[0]; + if (visible && visible[1] > 0) setCurrentSlide(visible[0].dataset.slide); + }, + { rootMargin: "-18% 0px -52% 0px", threshold: [0, 0.1, 0.25, 0.5, 0.75] }, + ); + for (const slide of slideCases) observer.observe(slide); +}