You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/08/23 09:16:44 UTC

(causeway) 09/22: adds theming kit analysis evidence

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch htmx
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit d80479e61d6145efda1f2626300a4faa76b93b97
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Aug 21 06:47:03 2026 +0100

    adds theming kit analysis evidence
---
 .../evidence/REPRODUCING.md                        |  109 +
 .../evidence/accessibility-and-visual-review.md    |   99 +
 .../evidence/adapter-assessment.md                 |   94 +
 .../evidence/adr.md                                |   70 +
 .../evidence/candidates.md                         |   90 +
 .../evidence/decision-matrix.md                    |   81 +
 .../evidence/evaluation-plan.md                    |  120 +
 .../evidence/follow-on-proposal-outline.md         |   50 +
 .../evidence/harness/.gitignore                    |    2 +
 .../evidence/harness/build-webawesome.mjs          |   37 +
 .../evidence/harness/candidates/baseline.css       |   23 +
 .../evidence/harness/candidates/bootstrap.css      |   14 +
 .../evidence/harness/candidates/open-props.css     |   22 +
 .../evidence/harness/candidates/web-awesome.css    |   18 +
 .../evidence/harness/capture.mjs                   |  352 ++
 .../evidence/harness/fixture.mjs                   |  448 +++
 .../harness/generated/webawesome-selective.css     |    1 +
 .../harness/generated/webawesome-selective.js      | 3944 ++++++++++++++++++++
 .../evidence/harness/harness.css                   |   65 +
 .../evidence/harness/index.html                    |   29 +
 .../evidence/harness/integrate-petclinic.mjs       |  166 +
 .../evidence/harness/measure-assets.mjs            |   87 +
 .../evidence/harness/package-lock.json             |  988 +++++
 .../evidence/harness/package.json                  |   23 +
 .../evidence/harness/prototype.html                |   14 +
 .../evidence/harness/server.mjs                    |   65 +
 .../evidence/harness/summarize-lighthouse.mjs      |   63 +
 .../evidence/harness/verify-assets.mjs             |   77 +
 .../evidence/harness/webawesome-entry.mjs          |   13 +
 .../evidence/inventory.md                          |   92 +
 .../evidence/migration-roadmap.md                  |   69 +
 .../evidence/packaging-assessment.md               |   80 +
 .../evidence/packaging/README.md                   |   57 +
 .../evidence/packaging/pom.xml                     |   83 +
 .../evidence/performance.md                        |   63 +
 .../evidence/results/asset-sizes.json              |  131 +
 .../evidence/results/asset-verification.json       |  109 +
 .../evidence/results/browser-evidence.json         |  852 +++++
 .../evidence/results/browser-evidence.md           |   52 +
 .../evidence/results/lighthouse-summary.json       |  535 +++
 .../evidence/results/lighthouse-summary.md         |  106 +
 .../evidence/results/packaging-verification.json   |   15 +
 .../evidence/results/real-viewer-integration.json  |  171 +
 .../evidence/results/real-viewer-integration.md    |   57 +
 .../evidence/results/security-audit.json           |   24 +
 .../screenshots/baseline-desktop-dark-prompt.jpg   |  Bin 0 -> 143321 bytes
 .../screenshots/baseline-desktop-light-menu.jpg    |  Bin 0 -> 147180 bytes
 .../screenshots/baseline-narrow-dark-all.jpg       |  Bin 0 -> 116628 bytes
 .../screenshots/baseline-narrow-light-nav.jpg      |  Bin 0 -> 128036 bytes
 .../screenshots/bootstrap-desktop-dark-prompt.jpg  |  Bin 0 -> 163533 bytes
 .../screenshots/bootstrap-desktop-light-menu.jpg   |  Bin 0 -> 160362 bytes
 .../screenshots/bootstrap-narrow-dark-all.jpg      |  Bin 0 -> 114053 bytes
 .../screenshots/bootstrap-narrow-light-nav.jpg     |  Bin 0 -> 136359 bytes
 .../evidence/screenshots/integration-baseline.jpg  |  Bin 0 -> 71448 bytes
 .../evidence/screenshots/integration-bootstrap.jpg |  Bin 0 -> 71594 bytes
 .../evidence/screenshots/integration-openprops.jpg |  Bin 0 -> 70862 bytes
 .../screenshots/integration-webawesome.jpg         |  Bin 0 -> 73917 bytes
 .../screenshots/openprops-desktop-dark-prompt.jpg  |  Bin 0 -> 174951 bytes
 .../screenshots/openprops-desktop-light-menu.jpg   |  Bin 0 -> 185797 bytes
 .../screenshots/openprops-narrow-dark-all.jpg      |  Bin 0 -> 149107 bytes
 .../screenshots/openprops-narrow-light-nav.jpg     |  Bin 0 -> 162308 bytes
 .../screenshots/webawesome-desktop-dark-prompt.jpg |  Bin 0 -> 148103 bytes
 .../screenshots/webawesome-desktop-light-menu.jpg  |  Bin 0 -> 162416 bytes
 .../screenshots/webawesome-narrow-dark-all.jpg     |  Bin 0 -> 128336 bytes
 .../screenshots/webawesome-narrow-light-nav.jpg    |  Bin 0 -> 139091 bytes
 .../evidence/spectrum-decision.md                  |   21 +
 .../analyze-web-component-theming-kit/tasks.md     |   40 +-
 67 files changed, 9701 insertions(+), 20 deletions(-)

diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/REPRODUCING.md b/openspec/changes/analyze-web-component-theming-kit/evidence/REPRODUCING.md
new file mode 100644
index 00000000000..14312ea5aa4
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/REPRODUCING.md
@@ -0,0 +1,109 @@
+# Reproducing the theming analysis
+
+## Prerequisites
+
+- Node.js 24 or another repository-supported Node version with native test and ES-module support.
+- npm with network access for the initial pinned dependency acquisition.
+- A Chromium browser supported by Playwright 1.61.0.
+- Maven and Java 17 or newer for the real Petclinic integration check.
+
+## Acquire and verify local assets
+
+From `openspec/changes/analyze-web-component-theming-kit/evidence/harness` run:
+
+```shell
+PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm ci --ignore-scripts --no-audit --no-fund
+npm run build-webawesome
+npm run verify-assets -- --write
+npm run measure-assets -- --write
+```
+
+`npm ci` verifies the package-lock integrity values and recreates `node_modules` without executing package lifecycle scripts.
+`verify-assets.mjs` checks exact direct versions and licenses, records lockfile integrity values, and hashes selected browser assets and license files.
+`build-webawesome.mjs` creates pinned selective JavaScript and CSS bundles for the twelve Web Awesome components exercised by the fixture.
+`measure-assets.mjs` records raw and gzip sizes for baseline, selective, browser-distribution, and candidate asset groups.
+
+If Chromium is not already installed for Playwright 1.61.0, run:
+
+```shell
+npx playwright install chromium
+```
+
+Browser installation is a developer prerequisite and is not a production application dependency.
+
+## Serve the harness
+
+Run:
+
+```shell
+npm run serve
+```
+
+The default URL is `http://127.0.0.1:4173/`.
+The built-in server exposes the repository root with path-containment checks and `Cache-Control: no-store` so local candidate and baseline assets can be compared without copying them or relying on a CDN.
+Set `PORT` or `HOST` to override the local listener.
+
+## Prototype URLs
+
+- Current Causeway: `/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline`.
+- Bootstrap: `/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap`.
+- Web Awesome: `/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome`.
+- Open Props and native primitives: `/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops`.
+
+Use `theme=light|dark`, `state=all|menu-open|prompt|responsive-nav`, and `motion=normal|reduce` query parameters to select deterministic evidence states.
+
+## Generate browser evidence
+
+Run:
+
+```shell
+node capture.mjs --write
+```
+
+The script starts the local server, launches Chromium, captures the required screenshot matrix, executes DOM and interaction assertions, records request and timing data, and writes retained evidence beneath `evidence/screenshots` and `evidence/results`.
+Set `PLAYWRIGHT_CHROMIUM_EXECUTABLE` when a specific Chromium executable is required.
+
+Run Lighthouse 13.1.0 in desktop and mobile modes for each candidate URL, save each JSON report as `evidence/lighthouse/<candidate>-<mode>/report.json`, and then run:
+
+```shell
+npm run summarize-lighthouse
+```
+
+The retained summary records category scores and every failed selector and explanation, while the reproducible raw reports can be discarded after summarization.
+
+## Real-viewer integration check
+
+Start the Petclinic application with the existing Maven profile and a supported JDK:
+
+```shell
+mvn -f viewers/webcomponents/pom.xml -Prun-sample-htmx-petclinic
+```
+
+With the application and harness servers running, execute:
+
+```shell
+npm run integrate-petclinic
+```
+
+The script records bounded candidate injection checks against `http://localhost:8080/htmx` without changing production source files.
+It serves injected assets through Playwright interception under a same-origin analysis path so the real viewer's content security policy remains enforced.
+The current production acceptance suite remains:
+
+```shell
+mvn -pl viewers/webcomponents/sample-htmx-petclinic -am \
+  -Pplaywright \
+  -Dtest=PetClinicHtmxApplication_IntegTest,PetClinicHtmxPlaywrightTest \
+  -Dsurefire.failIfNoSpecifiedTests=false \
+  test
+```
+
+## Cleanup
+
+Remove acquired and generated local-only files with:
+
+```shell
+rm -rf harness/node_modules harness/reports
+```
+
+Pinned `package.json`, `package-lock.json`, harness source, retained screenshots, and JSON or Markdown evidence remain version-controlled.
+No production Maven module or runtime asset is modified by this analysis harness.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/accessibility-and-visual-review.md b/openspec/changes/analyze-web-component-theming-kit/evidence/accessibility-and-visual-review.md
new file mode 100644
index 00000000000..e9cfad32db2
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/accessibility-and-visual-review.md
@@ -0,0 +1,99 @@
+# Accessibility and visual review
+
+## Evidence coverage
+
+The retained screenshot set contains four deterministic views for each baseline or candidate:
+
+- Desktop light with the application menu open.
+- Desktop dark with the action prompt open.
+- Narrow light with responsive navigation open.
+- Narrow dark with the full object, collection, and status fixture.
+
+The browser harness also exercised reduced motion and forced colors, checked structural labels and duplicate identifiers, captured console and network failures, and ran menu, prompt, tab, and focus journeys.
+Eight Lighthouse 13.1.0 audits covered desktop and mobile modes for all four strategies using Chrome 151.
+
+## Lighthouse results
+
+| Strategy | Desktop accessibility | Mobile accessibility | Contrast failures |
+|---|---:|---:|---:|
+| Current Causeway baseline fixture | 96 | 96 | 9 desktop and 14 mobile nodes |
+| Bootstrap CSS prototype | 96 | 96 | 6 desktop and 10 mobile nodes |
+| Web Awesome prototype | 100 | 100 | 0 |
+| Open Props and native prototype | 100 | 96 | 0 desktop and 27 mobile nodes |
+
+All strategies scored 100 for Lighthouse best practices.
+Every audit reported the fixture-only missing meta description as an SEO failure, which is common evidence-harness noise rather than a toolkit distinction.
+The retained `lighthouse-summary.json` contains every failed selector, snippet, and explanation.
+
+## Keyboard and focus results
+
+All four strategies passed the bounded application-menu journey:
+
+- The menu opened through its disclosure.
+- Escape closed the menu.
+- Focus returned to the disclosure.
+- Selecting Create closed the menu and opened the prompt.
+- Initial prompt focus reached the representative required input.
+- Escape closed the prompt and returned focus to the application-menu disclosure.
+- ArrowRight selected and focused the next tab.
+
+The Web Awesome adapter used `wa-input`, `wa-dialog`, `wa-dropdown`, and `wa-tab-group` focus behavior behind Causeway hosts and produced the same observable journey.
+The Open Props prototype used the native Popover top layer and native Dialog behavior without custom document-level dismissal code when Popover was supported.
+No strategy produced hidden focus, duplicate IDs, unlabeled ordinary inputs, unlabeled buttons, unnamed dialogs, console errors, failed resources, or page-level horizontal overflow in the retained browser run.
+
+## Forced colors and reduced motion
+
+Chromium confirmed `prefers-reduced-motion: reduce`, `forced-colors: active`, and dark color preference in the narrow preference journey for every strategy.
+All strategies retained visible content and responsive navigation with zero page-level overflow.
+The result proves media-query and browser-mode reachability, but manual Windows High Contrast and assistive-technology testing remains necessary before production adoption.
+
+## Visual facts
+
+### Current Causeway baseline
+
+- The baseline preserves the current compact Wicket-inspired information hierarchy and the strongest continuity with existing viewer screenshots.
+- Desktop cards, properties, collections, and status states align consistently.
+- The open menu and many ordinary buttons use a filled green treatment that gives primary and secondary controls insufficient differentiation.
+- The fixture token mapping produced dark text on the green action background, which accounts for the recorded contrast failures.
+- Narrow dark presentation is compact and readable, with internal table scrolling and no page overflow.
+
+### Bootstrap
+
+- Bootstrap provides immediately familiar buttons, cards, tables, spacing, and responsive density with very little candidate CSS.
+- The desktop layout is the most compact candidate and preserves the existing light-DOM structure.
+- Global Bootstrap typography and reset rules visibly alter code text, button fonts, borders, and radius in the real viewer even without adding Bootstrap classes everywhere.
+- `btn-outline-secondary` is too low contrast on the dark header and in dark surfaces without explicit contextual overrides.
+- Narrow dark layout is coherent, but disabled and secondary controls retain the recorded contrast limitations.
+
+### Web Awesome
+
+- Web Awesome provides the most consistent menu, tab, button, prompt, focus, disabled, and dark-mode component presentation in the fixture.
+- Lighthouse found no accessibility failure in either audited mode.
+- Spacing is more generous than the current viewer and increases narrow page length.
+- Toolkit controls do not style existing Causeway controls automatically because they render in toolkit-owned shadow DOM.
+- The full visual benefit therefore appears only where a Causeway renderer explicitly adopts a Web Awesome component.
+- The browser-ready autoloader generated many small module requests, which is unacceptable as a production delivery strategy without selective bundling.
+
+### Open Props and native primitives
+
+- Open Props offers the smallest and least coupled token source and gives native menu and dialog lifecycle a clear implementation path.
+- The light desktop prototype is coherent after a modest adapter stylesheet.
+- Open Props normalize and button packs are not a complete application theme and produce strong global typography and control changes.
+- Fluid heading defaults made the narrow page substantially taller and visually unbalanced.
+- The provisional dark token mapping combined poorly with generic button-pack colors and produced the largest mobile contrast failure set.
+- The candidate is viable only as a curated token source rather than as an unmodified normalize-and-buttons theme.
+
+## Preference observations
+
+The following are preferences rather than pass or fail findings:
+
+- Bootstrap looks familiar and dense but more generic.
+- Web Awesome looks polished and modern but less like the current Causeway viewer.
+- The Causeway baseline has stronger product continuity but more handcrafted control styling.
+- Open Props provides freedom rather than a finished visual identity.
+
+## Accessibility conclusion
+
+Web Awesome provides the strongest out-of-box component accessibility evidence but requires the largest adapter and delivery investment.
+Bootstrap and Open Props can preserve Causeway behavior, but neither removes the need for Causeway-owned contextual contrast and focus testing.
+The current baseline is already structurally sound, so wholesale component replacement is not justified solely by Lighthouse scores.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/adapter-assessment.md b/openspec/changes/analyze-web-component-theming-kit/evidence/adapter-assessment.md
new file mode 100644
index 00000000000..5b903c6bfdd
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/adapter-assessment.md
@@ -0,0 +1,94 @@
+# Semantic adapter and integration assessment
+
+## Boundary under test
+
+The desired boundary keeps application code, GraphQL contexts, semantic events, HTMX routing, and public `<causeway-*>` hosts independent of the selected visual implementation.
+Toolkit details may exist inside renderers, but they must not become required application markup or domain behavior.
+
+## Comparative assessment
+
+| Concern | Bootstrap CSS | Web Awesome | Open Props plus native |
+|---|---|---|---|
+| Public Causeway tags | Preserved | Preserved only through explicit wrappers | Preserved |
+| Semantic events | Causeway remains authoritative | Requires event translation per toolkit control | Causeway remains authoritative |
+| Focus and disabled state | Existing Causeway behavior retained | Toolkit properties and lifecycle must map to Causeway state | Existing behavior plus native Dialog and Popover |
+| Styling boundary | Global selectors and classes | Shadow DOM, custom properties, slots, and CSS parts | Global token packs plus Causeway selectors |
+| Application variables | Map Causeway variables to Bootstrap variables or compiled Sass | Map Causeway variables to `--wa-*` tokens and parts | Map Causeway variables to selected Open Props |
+| HTMX fragment lifecycle | CSS-only integration is naturally compatible | Custom elements reconnect safely but adapter lifecycle requires testing | Native and Causeway elements reconnect without framework state |
+| Automation | Existing selectors remain | Tests must cross Causeway hosts and toolkit shadow parts where necessary | Existing selectors remain |
+| Incremental adoption | Very easy but collision-prone | Explicit component-by-component migration | Very easy at token level |
+
+## Bootstrap assessment
+
+Bootstrap can improve existing light-DOM markup without replacing Causeway behavior.
+The least-coupled form is a compiled Causeway theme that consumes Bootstrap variables, mixins, or selected CSS layers while retaining Causeway class names.
+Adding Bootstrap classes directly to every renderer is faster initially but creates public-looking markup coupling and makes future removal expensive.
+Bootstrap dropdown, modal, collapse, and tab JavaScript should not be adopted while Causeway already owns those semantic state machines.
+Running both would create competing focus, dismissal, and lifecycle behavior.
+
+The real-viewer injection demonstrated that complete Bootstrap CSS changes generic button radius and font before purposeful adoption.
+A production proposal must use CSS layers, a selective Sass build, or strongly scoped imports rather than dropping the entire stylesheet into the shell.
+
+## Web Awesome assessment
+
+Web Awesome provides complete visual controls with strong audited behavior, but it does not theme existing Causeway buttons, inputs, menus, dialogs, or tabs automatically.
+Each adopted control needs a renderer adapter that maps Causeway state into toolkit attributes and properties and translates toolkit events back into Causeway semantic events.
+Focus delegation, asynchronous component definition, form association, validation messages, slots, parts, and open or close lifecycle events all require explicit tests.
+
+A representative mapping is:
+
+```text
+<causeway-action>
+    internal renderer
+        <wa-button>
+            shadow button
+```
+
+Applications continue addressing `<causeway-action>`, while the renderer owns `wa-button` and maps disabled reason, variant, busy state, invocation, focus, and result semantics.
+The same pattern can apply to prompts, inputs, selects, tabs, dropdowns, status callouts, and progress indicators.
+Collections and property grids still need Causeway-owned layout and ordinary table or grid semantics because Web Awesome does not replace the domain composition model.
+
+The browser-ready autoloader is unsuitable for production because the fixture loaded 132 candidate requests.
+The analysis produced a selective twelve-component bundle at 70,009 bytes gzip for JavaScript and 14,124 bytes gzip for toolkit CSS, proving that Maven-packaged bundling is feasible while also showing a material payload cost.
+Further component reduction or code splitting would be mandatory for any implementation proposal.
+
+## Open Props and native assessment
+
+Open Props can remain entirely behind `--causeway-*` values and Causeway selectors.
+It introduces no toolkit element or event vocabulary.
+Native Dialog and Popover can replace selected custom lifecycle code while keeping Causeway controls and semantic events.
+
+The strongest mapping is:
+
+```text
+--causeway-* public contract
+        ↓
+curated Causeway theme
+        ↓
+selected Open Props values
+
+Causeway menu or prompt host
+        ↓
+native popover or dialog lifecycle
+```
+
+The complete normalize and buttons packs should not be loaded globally because the prototype and real-viewer check showed substantial typography and control changes.
+A production proposal should import selected prop packs or copy resolved values into the generated Causeway theme.
+Popover's Baseline 2025 status requires either an explicit modern-browser policy or a small fallback using the current disclosure implementation.
+
+## Global CSS and shadow DOM trade-off
+
+Bootstrap and Open Props can style current light DOM immediately, which minimizes renderer work but increases collision risk.
+Web Awesome isolates visual implementation and avoids broad collisions, but every adopted component adds an adapter and can complicate inspection, CSS overrides, automation, and server-rendered first paint.
+
+## HTMX compatibility
+
+All candidates preserved route readiness, menu readiness, Escape dismissal, and zero page overflow in the bounded real Petclinic check.
+CSS-oriented strategies require no HTMX lifecycle integration.
+Web Awesome custom elements upgraded successfully when inserted after the route was ready, which supports fragment replacement, but production adoption still needs disconnect, reconnect, stale-response, and focus tests within actual HTMX swaps.
+
+## Contract conclusion
+
+Bootstrap CSS and Open Props can preserve the semantic boundary with a theme-only implementation.
+Web Awesome can preserve the boundary only through deliberate internal adapters and selective adoption.
+No candidate justifies exposing toolkit tags or events as public Causeway contracts.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/adr.md b/openspec/changes/analyze-web-component-theming-kit/evidence/adr.md
new file mode 100644
index 00000000000..f08c95da311
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/adr.md
@@ -0,0 +1,70 @@
+# ADR: Pilot a constrained Bootstrap-derived Causeway theme
+
+## Status
+
+Analysis recommendation for separate implementation review.
+
+## Context
+
+Causeway's semantic components already provide correct domain state, interaction events, HTMX lifecycle, keyboard behavior, and a substantial application-facing theme contract.
+The main opportunity is to reduce bespoke visual-system work without exposing a toolkit as the framework's public component API.
+The evaluated candidates were Bootstrap 5.3, Web Awesome 3.11, and Open Props 1.7 with native Dialog and Popover, with the current Causeway implementation as baseline.
+
+## Decision
+
+Recommend a constrained Bootstrap-derived theme pilot rather than a full component-toolkit migration.
+
+The pilot will:
+
+- Retain public `<causeway-*>` elements, semantic events, GraphQL contexts, HTMX routing, and `--causeway-*` variables.
+- Use selected Bootstrap Sass variables, functions, and mixins or equivalently scoped generated rules to implement Causeway-owned selectors.
+- Keep Bootstrap classes and implementation details undocumented and non-contractual if any are used internally.
+- Exclude Bootstrap JavaScript, Popper, dropdown, modal, collapse, and tab state machines.
+- Package generated CSS through Maven with pinned source and reproducible checksums.
+- Begin as an opt-in Petclinic and vanilla-sample theme pilot rather than changing the default theme immediately.
+- Fix every audited contrast failure and meet the existing responsive, dark, reduced-motion, forced-colors, focus, overflow, and Playwright acceptance gates.
+
+Native Dialog remains appropriate for modal prompts.
+Native Popover should be evaluated independently once Causeway adopts an explicit browser floor or retains the current disclosure fallback.
+
+## Rationale
+
+Bootstrap produced the highest weighted score at 4.10 and the strongest pragmatic balance of visual coverage, maintenance, packaging, and current architectural compatibility.
+It achieved a compact familiar viewer with only a small adapter stylesheet, and the repository already has Bootstrap 5.3.8 in its wider Maven ecosystem.
+The score margin is small, so the decision is a pilot with measurable exit criteria rather than irreversible adoption.
+
+Web Awesome produced the strongest Lighthouse and component polish evidence but requires explicit adapters, shadow-DOM-aware tests, a frontend bundle, and approximately 84.8 KB gzip for the twelve-component selective fixture bundle.
+Its value may become stronger if Causeway later chooses to replace internal control renderers, but it is not the lowest-risk theming step.
+
+Open Props provides excellent performance and contract compatibility, but it is a token source rather than a finished application design system.
+The prototype required substantial Causeway CSS and produced the weakest narrow typography and dark control mapping before customization.
+
+## Consequences
+
+### Positive
+
+- Causeway can reuse a mature design system without replacing its semantic architecture.
+- The Wicket and web-component viewers can converge visually without sharing runtime behavior.
+- Maven and WebJar compatibility remain available.
+- The pilot can be removed by dropping generated CSS and restoring the current theme.
+- Existing application customization continues through Causeway variables.
+
+### Negative
+
+- A selective Sass or CSS-generation step introduces frontend build maintenance.
+- Bootstrap assumptions can leak through light-DOM classes or global selectors unless the build is carefully scoped.
+- Causeway still owns semantic behavior, domain composition, and contextual accessibility testing.
+- Applications may observe internal markup even when it is documented as non-contractual.
+- The existing specification language rejecting Bootstrap markup may need clarification for internal generated theme implementation.
+
+## Rejected alternatives
+
+- Full Bootstrap CSS and JavaScript adoption was rejected because it creates global collisions and duplicate interaction state machines.
+- Broad Web Awesome replacement was rejected because of adapter, bundle, dependency, and shadow DOM costs.
+- Open Props as the sole theme system was rejected because it does not supply enough component-level visual decisions.
+- Keeping the current theme unchanged was rejected as the only next step because the evidence supports a low-risk Bootstrap-derived pilot with clear rollback.
+
+## Review trigger
+
+The pilot must be reconsidered if selective Bootstrap styling cannot remain below the agreed CSS budget, cannot avoid public contract leakage, or cannot outperform the baseline in accessibility and maintainability evidence.
+Web Awesome may be reconsidered for a narrow control family if a selective bundle meets budget and removes more custom behavior than its adapter introduces.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/candidates.md b/openspec/changes/analyze-web-component-theming-kit/evidence/candidates.md
new file mode 100644
index 00000000000..5f6b9c3372b
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/candidates.md
@@ -0,0 +1,90 @@
+# Candidate freeze and supply-chain baseline
+
+## Evaluation date
+
+Candidate metadata was captured from official documentation and the npm registry on 2026-08-20.
+The analysis uses exact versions rather than floating tags.
+
+## Primary candidates
+
+| Strategy | Frozen package | License | Distribution | Direct runtime dependencies |
+|---|---|---|---|---|
+| Bootstrap CSS and utilities | `bootstrap@5.3.8` | MIT | Compiled CSS, JavaScript, Sass, npm tarball, CDN, and Maven WebJar | CSS has none; interactive dropdown, popover, and tooltip behavior requires Bootstrap JavaScript and Popper |
+| Web Awesome adapters | `@awesome.me/webawesome@3.11.0` | MIT for the evaluated core npm package | Selective ES modules, bundled browser-ready `dist-cdn`, CSS themes, utilities, npm tarball | Lit, Lit SSR packages, Floating UI DOM, TinyColor, localization and animation helpers, QR helper, Marked, Nano ID, and adapter helpers |
+| Open Props plus native primitives | `open-props@1.7.23` | MIT | Individual CSS prop packs, combined CSS, JSON design tokens, npm tarball, CDN | None reported by npm metadata |
+
+## Integrity records
+
+- `bootstrap@5.3.8` tarball: `https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz`.
+- `bootstrap@5.3.8` integrity: `sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==`.
+- `@awesome.me/webawesome@3.11.0` tarball: `https://registry.npmjs.org/@awesome.me/webawesome/-/webawesome-3.11.0.tgz`.
+- `@awesome.me/webawesome@3.11.0` integrity: `sha512-WM8kyP+AdAT3/zwNpc/YzN3Nu5pRD6ksREFZAct/DIn6KMnQs8lPVfiPbJrOpKMk8OaSnQX19iiwFGjoswXVXw==`.
+- `open-props@1.7.23` tarball: `https://registry.npmjs.org/open-props/-/open-props-1.7.23.tgz`.
+- `open-props@1.7.23` integrity: `sha512-+xyrJmxV9QUBFbSwPROYhOg/FLXry+uuPr4R+B5EaE4556Oc18/8ZC/fL5A+/lbRSwNvA0Alh+C0KpyFWLmLZg==`.
+
+## Primary candidate notes
+
+### Bootstrap 5.3.8
+
+Bootstrap is actively maintained and already appears in the repository's Wicket dependency management and local Maven dependency graph as WebJar `org.webjars:bootstrap:5.3.8`.
+The analysis uses compiled CSS variables and selected utility or component classes while preserving Causeway-owned disclosure, dialog, and domain behavior.
+Bootstrap documentation states that dropdowns, popovers, and tooltips require Bootstrap JavaScript and Popper, while CSS-only styling does not.
+A production proposal could use an explicit Maven WebJar dependency and would not need npm, but it must account for global selectors, reset behavior, class coupling, and toolkit-specific markup.
+
+### Web Awesome 3.11.0
+
+Web Awesome is the active successor to Shoelace and provides themes, utilities, and Web Components.
+The evaluated npm package is MIT licensed and supports selective ES-module imports and a browser-ready `dist-cdn` distribution.
+The official service also advertises optional Pro installation paths, but this analysis evaluates only the MIT core npm package and must not depend on commercial assets.
+Web Awesome's shadow DOM requires documented CSS custom properties and `::part()` selectors rather than ordinary descendant selectors.
+Its runtime dependency surface is materially larger than the other primary candidates and requires a Maven-integrated npm or vendored browser-distribution strategy.
+
+### Open Props 1.7.23 with native Dialog and Popover
+
+Open Props supplies design tokens rather than a component lifecycle or accessibility layer.
+The candidate uses pinned local CSS files and Causeway-owned semantic markup.
+Native `<dialog>` supplies modal behavior, while the Popover API supplies top-layer non-modal presentation, light dismissal, Escape handling, and `showPopover()`, `hidePopover()`, or declarative target control.
+MDN reports Popover as Baseline 2025, so the unresolved Causeway browser floor is a hard adoption question rather than an assumed pass.
+This strategy has the smallest third-party runtime dependency surface but leaves Causeway responsible for composition, semantic mapping, and unsupported-browser fallback.
+
+## Fallback benchmark
+
+`@spectrum-web-components/bundle@1.12.2` is Apache-2.0 licensed and exposes a broad Lit-based component catalogue.
+The complete bundle has a very large direct package graph, and official guidance recommends selective component imports rather than the all-components bundle.
+Spectrum remains a fallback benchmark only if the primary candidates fail to establish a credible component-toolkit comparison.
+
+## Comparison-only and rejected candidates
+
+### Material Web 2.5.0
+
+`@material/web@2.5.0` is Apache-2.0 licensed and depends on Lit, `@lit/context`, and `tslib`.
+Official project documentation currently describes Material Web as being in maintenance mode pending new maintainers.
+It remains a comparison baseline and is not a preferred strategic dependency.
+
+### Shoelace 2.20.1
+
+`@shoelace-style/shoelace@2.20.1` is MIT licensed, but the official site states that Shoelace is sunset with no active development and directs new work to Web Awesome.
+It is excluded as an independent candidate.
+
+## Governance and maintenance gates
+
+A candidate fails before scoring if any of these conditions applies:
+
+- Its production distribution is not compatible with ASF dependency and notice policy.
+- Required assets cannot be pinned, verified, and built without runtime CDN access.
+- The evaluated feature requires commercial-only assets or an undisclosed service dependency.
+- Security updates or project continuity have no credible maintenance path.
+- The toolkit requires public Causeway domain contracts to depend on toolkit-specific semantics without a separately reviewed specification change.
+
+## Sources
+
+- Bootstrap documentation: `https://getbootstrap.com/docs/5.3/`.
+- Bootstrap npm metadata: `https://www.npmjs.com/package/bootstrap/v/5.3.8`.
+- Web Awesome documentation: `https://webawesome.com/docs/`.
+- Web Awesome npm metadata: `https://www.npmjs.com/package/@awesome.me/webawesome/v/3.11.0`.
+- Open Props documentation: `https://open-props.style/`.
+- Open Props npm metadata: `https://www.npmjs.com/package/open-props/v/1.7.23`.
+- Spectrum Web Components documentation: `https://opensource.adobe.com/spectrum-web-components/`.
+- Material Web repository: `https://github.com/material-components/material-web`.
+- Shoelace installation and project status: `https://shoelace.style/getting-started/installation`.
+- Popover API baseline and behavior: `https://developer.mozilla.org/en-US/docs/Web/API/Popover_API`.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/decision-matrix.md b/openspec/changes/analyze-web-component-theming-kit/evidence/decision-matrix.md
new file mode 100644
index 00000000000..b43f3ede4df
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/decision-matrix.md
@@ -0,0 +1,81 @@
+# Hard gates and weighted decision matrix
+
+## Hard gates
+
+| Strategy | License and provenance | Offline Maven path | No required commercial feature | Causeway semantic boundary | Accessibility | Browser support or fallback | Result |
+|---|---|---|---|---|---|---|---|
+| Current Causeway baseline | Pass | Pass | Pass | Pass | Pass with known contrast work | Pass under current Chromium contract | Pass |
+| Bootstrap CSS-only or Sass-derived | Pass | Pass through explicit WebJar or Maven build | Pass | Pass only while Bootstrap JavaScript and public toolkit contracts remain excluded | Pass with contextual contrast fixes | Pass | Pass with scope constraint |
+| Web Awesome selective adapters | Pass for MIT core; full transitive report still required | Pass through pinned npm and selective Maven bundle | Pass for evaluated core | Pass only through explicit internal adapters | Pass in fixture | Pass for current evergreen Chromium; wider policy still required | Pass with adapter and bundle constraints |
+| Open Props plus native primitives | Pass | Pass through selected CSS packaging | Pass | Pass | Pass after curated dark and control mappings | Pass only with current disclosure fallback until browser floor is explicit | Pass with fallback |
+
+Material Web remains comparison-only because of maintenance status.
+Shoelace fails the active-maintenance gate.
+
+## Scores
+
+Scores range from 0 to 5 and use the weights defined before prototype work.
+
+| Strategy | Accessibility 25% | Compatibility 20% | Maintenance 15% | Packaging 15% | Visual and theming 15% | Performance 10% | Weighted total |
+|---|---:|---:|---:|---:|---:|---:|---:|
+| Current Causeway baseline | 3.5 | 5.0 | 3.0 | 5.0 | 3.5 | 4.0 | **4.00** |
+| Bootstrap constrained theme | 3.5 | 4.0 | 4.5 | 5.0 | 4.0 | 4.0 | **4.10** |
+| Web Awesome selective adapters | 5.0 | 2.5 | 4.0 | 3.0 | 4.5 | 2.5 | **3.73** |
+| Open Props and native primitives | 3.5 | 5.0 | 4.0 | 4.5 | 2.5 | 5.0 | **4.03** |
+
+## Score evidence
+
+### Accessibility and interaction correctness
+
+- Web Awesome scored 100 in desktop and mobile Lighthouse audits and passed the common focus journeys.
+- Baseline and Bootstrap scored 96 because of contextual contrast failures while retaining correct menu, prompt, tab, and overflow behavior.
+- Open Props scored 100 desktop and 96 mobile, with 27 dark mobile contrast failures caused by incomplete token and button-pack integration.
+- Evidence: `accessibility-and-visual-review.md`, `results/browser-evidence.json`, and `results/lighthouse-summary.json`.
+
+### Semantic and architectural compatibility
+
+- Baseline is the contract being preserved.
+- Bootstrap CSS can retain Causeway behavior but creates global collision and internal class pressure.
+- Web Awesome needs adapter code for every adopted control and introduces shadow DOM boundaries.
+- Open Props can remain a private token source and native primitives can preserve Causeway events.
+- Evidence: `adapter-assessment.md` and `results/real-viewer-integration.json`.
+
+### Maintenance and governance
+
+- Bootstrap has the most mature maintenance and broadest existing ecosystem.
+- Web Awesome is active and MIT licensed but is a newer successor with a 35-instance resolved runtime dependency closure.
+- Open Props has a small dependency surface but provides fewer maintained component decisions.
+- Baseline avoids upstream risk but leaves all design-system maintenance with Causeway.
+- Evidence: `candidates.md`, `packaging-assessment.md`, and `results/security-audit.json`.
+
+### Packaging and build integration
+
+- Baseline and Bootstrap fit the existing Maven and WebJar model directly.
+- Open Props is small and packages easily, although the indexed WebJar trails the frozen release.
+- Web Awesome requires pinned npm acquisition and bundling but the analysis built and packaged selective assets successfully.
+- Evidence: `packaging/README.md`, `results/packaging-verification.json`, and `results/asset-verification.json`.
+
+### Visual and theming capability
+
+- Web Awesome provides the most complete coherent control-level design system.
+- Bootstrap gives a familiar compact result quickly and aligns with the Wicket viewer's design lineage.
+- Baseline is coherent and product-specific but visibly handcrafted in control hierarchy.
+- Open Props supplies ingredients rather than a complete visual system and needs the most Causeway-authored theme work.
+- Evidence: retained screenshots and `accessibility-and-visual-review.md`.
+
+### Performance
+
+- Open Props has a 12,028-byte-gzip candidate theme payload.
+- Bootstrap CSS plus adapter is 31,084 bytes gzip, excluding optional JavaScript.
+- The selective twelve-component Web Awesome bundle is 84,848 bytes gzip including toolkit CSS and the adapter.
+- Web Awesome's unbundled browser distribution generated 132 requests and is rejected for production.
+- Evidence: `performance.md` and `results/asset-sizes.json`.
+
+## Decision interpretation
+
+Bootstrap's 4.10 score is only 0.10 above the current baseline and 0.075 above Open Props.
+That margin does not justify a wholesale rewrite or a public toolkit contract.
+It does justify a constrained implementation pilot because Bootstrap provides the strongest combination of visual coverage, mature maintenance, Maven packaging, and continuity with the Wicket-inspired viewer.
+
+Web Awesome is not selected for broad adoption because its adapter and payload costs outweigh its stronger component-level accessibility and polish for the current semantic architecture.
+Open Props remains a useful token and native-platform reference, but the prototype shows that it does not remove enough Causeway-authored design work to serve as the primary theming kit.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/evaluation-plan.md b/openspec/changes/analyze-web-component-theming-kit/evidence/evaluation-plan.md
new file mode 100644
index 00000000000..e5336d9bb83
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/evaluation-plan.md
@@ -0,0 +1,120 @@
+# Evaluation plan
+
+## Principle
+
+Every candidate and the current Causeway baseline will render the same state catalogue and will be assessed with the same procedures.
+Hard gates apply before weighted scores.
+Evidence is retained under this change so the analysis moves with its archive.
+
+## Fixture catalogue
+
+The analysis harness will expose these stable state identifiers:
+
+| State | Required content |
+|---|---|
+| `shell` | Brand, primary and secondary navigation, main region, result region, and footer |
+| `menu-open` | Expanded application menu, enabled actions, disabled action with reason, long label, and keyboard focus |
+| `prompt` | Modal action prompt, text input, select input, multiline input, validation message, submit, and cancel |
+| `object` | Object title, object actions, associated actions, tabs, property labels and values, editable controls, and status metadata |
+| `collection` | Compact table, object links, numeric and date values, empty alternative, and constrained overflow |
+| `status` | Loading, ready, partial-error, validation-error, empty, unsupported, and terminal-error presentations |
+| `responsive-nav` | Collapsed and expanded narrow navigation with nested application menu |
+
+Each state will include deliberately difficult content such as long labels, long unbroken values, disabled controls, validation failures, empty data, partial errors, and enough table columns to test overflow.
+
+## Viewports and preferences
+
+- Desktop viewport: `1440 × 900` CSS pixels.
+- Narrow viewport: `390 × 844` CSS pixels.
+- Required color modes: explicit light and explicit dark.
+- Required motion modes: normal and `prefers-reduced-motion: reduce`.
+- Required contrast mode: `forced-colors: active` where Chromium emulation or host support permits it.
+- Required scale check: 200% browser zoom or an equivalent `2×` text and layout check for the leading strategies.
+
+The full Cartesian product is not required for every state.
+At minimum, shell, menu, prompt, object, collection, and status evidence is captured in desktop light, desktop dark, narrow light, and narrow dark, with separate reduced-motion and forced-colors journeys for interactive states.
+
+## Browser matrix
+
+- Required executable browser: the Chromium version supplied or selected by Playwright for Java 1.61.0.
+- Required secondary compatibility review: current Firefox and WebKit support statements for every adopted API and package.
+- Optional executable browsers: Firefox and WebKit when the local Playwright installation already provides them.
+- Unresolved policy gate: Causeway must define a minimum browser floor before relying on Baseline 2025 APIs such as Popover without fallback.
+
+A candidate cannot receive a full browser-compatibility score solely from Chromium execution.
+
+## Keyboard and focus journeys
+
+1. Tab to the primary menu disclosure, open it with Enter or Space, move through items, invoke one action, and verify dismissal and resulting focus.
+2. Open a menu and press Escape, then verify disclosure state, panel visibility, and restored focus.
+3. Open the responsive navigation, open a nested menu, dismiss it, and verify that no focused element becomes hidden.
+4. Open the prompt, verify initial focus, traverse every control, trigger validation, verify invalid-field focus, cancel with button and Escape, and verify invoking-control focus.
+5. Traverse tabs with keyboard controls and verify selected state, controlled panel visibility, and focus order.
+6. Traverse editable properties, disabled controls with explanations, collection links, error actions, and result presentation.
+7. Repeat the menu and prompt journeys in narrow layout and forced-colors mode.
+
+## Automated checks
+
+- Chrome Lighthouse accessibility audit for every candidate's desktop shell and narrow interactive state.
+- DOM assertions for accessible names, `aria-expanded`, controlled-panel visibility, dialog modality, selected tabs, validation relationships, hidden focus, and page overflow.
+- Console and network failure capture.
+- Screenshot capture with fixed viewport, state, color mode, and candidate naming.
+- Asset inventory and compressed-size calculation.
+- Navigation and rendering timing capture after a warm-up load and at least five measured reloads.
+
+## Performance metrics
+
+- Raw and gzip-compressed CSS bytes.
+- Raw and gzip-compressed JavaScript bytes.
+- Number of candidate-owned CSS, JavaScript, font, icon, and image requests.
+- Total candidate-owned transfer bytes for a cold local load.
+- DOM content loaded, load, first contentful paint, and largest contentful paint where available.
+- Custom-element definition and fixture-ready timing for Web Component candidates.
+- Median of at least five measured runs after one warm-up run.
+- Separate full-distribution and selective-import measurements when both are realistic production options.
+
+## Weighted score
+
+| Category | Weight | Evidence |
+|---|---:|---|
+| Accessibility and interaction correctness | 25% | Lighthouse, DOM assertions, manual keyboard and focus journeys |
+| Semantic and architectural compatibility | 20% | Adapter map, contract leakage, HTMX lifecycle, styling boundary |
+| Maintenance and governance | 15% | License, release status, dependencies, security and update path |
+| Packaging and build integration | 15% | Offline Maven path, lockfile, generated assets, release implications |
+| Visual and theming capability | 15% | Comparable screenshots, preferences, branding, override surface |
+| Performance | 10% | Compressed size, requests, startup and rendering measurements |
+
+Every category is scored from 0 to 5 and multiplied by its weight.
+A score must link to evidence and cannot compensate for a failed hard gate.
+
+## Hard gates
+
+- ASF-compatible license, provenance, notices, and dependency governance.
+- Pinned production assets with no runtime CDN requirement.
+- No commercial-only feature required for the recommended scope.
+- No disclosure of sensitive application or domain data.
+- Preservation of Causeway domain semantics and a credible adapter boundary.
+- Operable keyboard and focus behavior with no critical automated accessibility failure.
+- Credible browser support or documented progressive fallback for required APIs.
+
+## Visual review rubric
+
+Reviewers record facts separately from preferences.
+Facts include clipping, overflow, contrast failure, hidden focus, unstyled states, collision, missing responsive adaptation, and unsupported controls.
+Preferences include density, visual warmth, brand character, radius, typography, and familiarity.
+The final recommendation may cite preferences only after factual failures and architectural costs are accounted for.
+
+## Reproduction output
+
+A complete evidence run produces:
+
+- Candidate metadata and integrity records.
+- Baseline and candidate screenshots.
+- Lighthouse reports.
+- Keyboard and focus results.
+- DOM assertion results.
+- Console and network failure results.
+- Asset and performance measurements.
+- Adapter and packaging assessments.
+- Weighted matrix and hard-gate decision.
+- Architectural decision record and migration outline.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/follow-on-proposal-outline.md b/openspec/changes/analyze-web-component-theming-kit/evidence/follow-on-proposal-outline.md
new file mode 100644
index 00000000000..53914a4796f
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/follow-on-proposal-outline.md
@@ -0,0 +1,50 @@
+# Follow-on proposal outline: add Bootstrap-derived Causeway theme pilot
+
+## Suggested change name
+
+`add-bootstrap-derived-web-component-theme-pilot`
+
+## Why
+
+The toolkit analysis found that a constrained Bootstrap-derived theme offers the strongest pragmatic balance of visual coverage, mature maintenance, Maven compatibility, and preservation of Causeway semantic components.
+The score margin over the current theme and Open Props is small, so an opt-in evidence-driven pilot is warranted instead of immediate default-theme replacement.
+
+## What Changes
+
+- Add a pinned and reproducible selective Bootstrap Sass or scoped CSS build invoked by Maven.
+- Generate one Causeway-owned pilot stylesheet without Bootstrap JavaScript or Popper.
+- Preserve public `<causeway-*>` elements, semantic events, GraphQL contexts, HTMX lifecycle, and application-facing `--causeway-*` variables.
+- Cover shell, menus, prompts, buttons, forms, validation, tabs, properties, collections, tables, loading, results, errors, and responsive navigation.
+- Enable the theme only in the HTMX Petclinic and vanilla samples during the pilot.
+- Add visual, accessibility, responsive, preference, packaging, size, and Playwright acceptance gates.
+- Retain the current theme as the default and rollback path.
+
+## Expected capability impact
+
+### New capability
+
+- `bootstrap-derived-web-component-theme`: Defines the opt-in generated theme, packaging, customization boundary, budgets, and acceptance behavior.
+
+### Modified capability
+
+- `generic-htmx-web-component-viewer`: Clarifies that internal theme generation may use Bootstrap design sources while public semantic components and documented customization remain toolkit-neutral.
+
+## Design questions for proposal creation
+
+- Use Bootstrap WebJar Sass sources or a repository-owned npm lock and Sass compiler.
+- Decide whether any internal Bootstrap classes are acceptable or all rules must compile into Causeway selectors.
+- Define the theme selection configuration and asset URL.
+- Decide which existing `--causeway-*` variables require additions or compatibility aliases.
+- Confirm browser floor and whether native Popover is a separate follow-on concern.
+- Set screenshot baselines and Lighthouse policy for sample content.
+
+## Acceptance summary
+
+- Generated CSS is no more than 40 KB gzip.
+- The pilot adds no Bootstrap JavaScript.
+- Representative Lighthouse accessibility reaches 100 after contextual contrast fixes.
+- Desktop and narrow layouts have no page-level overflow.
+- Light, dark, reduced-motion, and forced-colors states remain operable.
+- Existing foundation and Petclinic Playwright tests pass.
+- Applications that do not opt in remain unchanged.
+- Removing the pilot stylesheet restores the previous presentation without data or API migration.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/.gitignore b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/.gitignore
new file mode 100644
index 00000000000..02d4b8b1003
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+reports/
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/build-webawesome.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/build-webawesome.mjs
new file mode 100644
index 00000000000..668878931f3
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/build-webawesome.mjs
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {mkdirSync} from 'node:fs';
+import {resolve} from 'node:path';
+import {fileURLToPath} from 'node:url';
+import {build} from 'esbuild';
+
+const directory = fileURLToPath(new URL('.', import.meta.url));
+const outputDirectory = resolve(directory, 'generated');
+mkdirSync(outputDirectory, {recursive: true});
+
+await build({
+  entryPoints: [resolve(directory, 'webawesome-entry.mjs')],
+  bundle: true,
+  format: 'esm',
+  minify: true,
+  outfile: resolve(outputDirectory, 'webawesome-selective.js'),
+  platform: 'browser',
+  sourcemap: false,
+  treeShaking: true
+});
+
+await build({
+  entryPoints: [resolve(directory, 'node_modules/@awesome.me/webawesome/dist/styles/webawesome.css')],
+  bundle: true,
+  loader: {'.woff2': 'dataurl'},
+  minify: true,
+  outfile: resolve(outputDirectory, 'webawesome-selective.css')
+});
+
+console.log('Built generated/webawesome-selective.js and generated/webawesome-selective.css');
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/baseline.css b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/baseline.css
new file mode 100644
index 00000000000..303170d6751
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/baseline.css
@@ -0,0 +1,23 @@
+body { background: var(--causeway-page-background); color: var(--causeway-text); font-family: var(--causeway-font-family); line-height: var(--causeway-line-height); }
+.fixture-header { background: var(--causeway-nav-background); color: var(--causeway-nav-text); box-shadow: var(--causeway-shadow-sm); }
+.fixture-mark { background: var(--causeway-link); color: Canvas; }
+.fixture-main { max-width: var(--causeway-content-width); }
+.fixture-card, .fixture-status, .fixture-menu-panel { background: var(--causeway-surface); border: var(--causeway-border); border-radius: var(--causeway-radius-md); box-shadow: var(--causeway-shadow-sm); }
+.fixture-card { margin-block-end: var(--causeway-space-4); }
+.fixture-menu-panel { background: var(--causeway-menu-panel-background); color: var(--causeway-menu-panel-color); box-shadow: var(--causeway-menu-panel-shadow); }
+button, .fixture-button { min-height: var(--causeway-control-height); border: var(--causeway-border); border-radius: var(--causeway-radius-sm); background: var(--causeway-action-background); color: var(--causeway-text); padding: var(--causeway-space-2) var(--causeway-space-3); }
+button:hover:not(:disabled) { background: var(--causeway-action-background-hover); }
+button[data-kind="primary"] { background: var(--causeway-link); border-color: var(--causeway-link); color: Canvas; }
+button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: .2rem solid var(--causeway-focus); outline-offset: .12rem; }
+input, select, textarea { inline-size: 100%; min-height: var(--causeway-control-height); border: var(--causeway-border); border-radius: var(--causeway-radius-sm); background: var(--causeway-surface); color: var(--causeway-text); padding: var(--causeway-space-2); }
+.fixture-eyebrow, .fixture-property-label { color: var(--causeway-muted); }
+.fixture-error { color: var(--causeway-danger); }
+.fixture-status[data-tone="success"] { border-inline-start: .35rem solid var(--causeway-success); }
+.fixture-status[data-tone="warning"] { border-inline-start: .35rem solid var(--causeway-warning); }
+.fixture-status[data-tone="danger"] { border-inline-start: .35rem solid var(--causeway-danger); }
+.fixture-tabs [role="tab"][aria-selected="true"] { border-color: var(--causeway-object-tab-accent); box-shadow: inset 0 -.2rem var(--causeway-object-tab-accent); }
+.fixture-table th { background: var(--causeway-surface-subtle); }
+.fixture-table th, .fixture-table td { border-block-end: var(--causeway-border); }
+dialog { background: var(--causeway-surface-raised); color: var(--causeway-text); border: var(--causeway-border-strong); border-radius: var(--causeway-radius-lg); box-shadow: var(--causeway-shadow-md); }
+dialog::backdrop { background: color-mix(in srgb, #000 55%, transparent); }
+.fixture-footer { background: var(--causeway-surface-subtle); color: var(--causeway-muted); }
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/bootstrap.css b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/bootstrap.css
new file mode 100644
index 00000000000..cc1326d602f
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/bootstrap.css
@@ -0,0 +1,14 @@
+:root { --fixture-shell-width: 82rem; --fixture-content-width: 76rem; }
+body { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }
+.fixture-header { background: var(--bs-dark); color: var(--bs-white); box-shadow: var(--bs-box-shadow-sm); }
+.fixture-mark { background: var(--bs-primary); color: var(--bs-white); }
+.fixture-card, .fixture-status, .fixture-menu-panel { background: var(--bs-body-bg); border: var(--bs-border-width) solid var(--bs-border-color); border-radius: var(--bs-border-radius); box-shadow: var(--bs-box-shadow-sm); }
+.fixture-menu-panel { --bs-dropdown-zindex: 1000; }
+.fixture-eyebrow, .fixture-property-label { color: var(--bs-secondary-color); }
+.fixture-error { color: var(--bs-danger-text-emphasis); }
+.fixture-status[data-tone="success"] { border-inline-start: .35rem solid var(--bs-success); }
+.fixture-status[data-tone="warning"] { border-inline-start: .35rem solid var(--bs-warning); }
+.fixture-status[data-tone="danger"] { border-inline-start: .35rem solid var(--bs-danger); }
+.fixture-tabs [role="tablist"] { border-block-end: var(--bs-border-width) solid var(--bs-border-color); }
+.fixture-table-region { border-radius: var(--bs-border-radius); }
+.fixture-footer { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); }
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/open-props.css b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/open-props.css
new file mode 100644
index 00000000000..1a2835a4656
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/open-props.css
@@ -0,0 +1,22 @@
+:root { --fixture-shell-width: 82rem; --fixture-content-width: 76rem; --causeway-props-brand: var(--indigo-7); --causeway-props-surface: white; --causeway-props-raised: var(--gray-1); --causeway-props-text: var(--gray-9); --causeway-props-muted: var(--gray-7); --causeway-props-border: var(--gray-4); --causeway-props-focus: var(--cyan-5); }
+html[data-theme="dark"] { --causeway-props-brand: var(--indigo-4); --causeway-props-surface: var(--gray-9); --causeway-props-raised: var(--gray-8); --causeway-props-text: var(--gray-1); --causeway-props-muted: var(--gray-4); --causeway-props-border: var(--gray-7); --causeway-props-focus: var(--cyan-3); }
+body { background: var(--causeway-props-raised); color: var(--causeway-props-text); font-family: var(--font-sans); line-height: var(--font-lineheight-3); }
+.fixture-header { background: var(--causeway-props-brand); color: white; box-shadow: var(--shadow-2); }
+.fixture-mark { background: white; color: var(--causeway-props-brand); }
+.fixture-card, .fixture-status, .fixture-menu-panel { background: var(--causeway-props-surface); border: var(--border-size-1) solid var(--causeway-props-border); border-radius: var(--radius-2); box-shadow: var(--shadow-2); }
+.fixture-menu-panel:popover-open { display: grid; gap: var(--size-1); inset: unset; position: absolute; }
+button { border-radius: var(--radius-2); padding: var(--size-2) var(--size-3); }
+button[data-kind="primary"] { background: var(--causeway-props-brand); border-color: var(--causeway-props-brand); color: white; }
+button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: var(--border-size-3) solid var(--causeway-props-focus); outline-offset: var(--size-1); }
+input, select, textarea { inline-size: 100%; border: var(--border-size-1) solid var(--causeway-props-border); border-radius: var(--radius-2); background: var(--causeway-props-surface); color: var(--causeway-props-text); padding: var(--size-2); }
+.fixture-eyebrow, .fixture-property-label { color: var(--causeway-props-muted); }
+.fixture-error { color: var(--red-7); }
+.fixture-status[data-tone="success"] { border-inline-start: var(--border-size-4) solid var(--green-7); }
+.fixture-status[data-tone="warning"] { border-inline-start: var(--border-size-4) solid var(--orange-6); }
+.fixture-status[data-tone="danger"] { border-inline-start: var(--border-size-4) solid var(--red-7); }
+.fixture-tabs [role="tab"][aria-selected="true"] { border-color: var(--causeway-props-brand); box-shadow: inset 0 calc(var(--border-size-3) * -1) var(--causeway-props-brand); }
+.fixture-table th { background: var(--causeway-props-raised); }
+.fixture-table th, .fixture-table td { border-block-end: var(--border-size-1) solid var(--causeway-props-border); }
+dialog { background: var(--causeway-props-surface); color: var(--causeway-props-text); border: var(--border-size-1) solid var(--causeway-props-border); border-radius: var(--radius-3); box-shadow: var(--shadow-5); }
+dialog::backdrop { background: hsl(0 0% 0% / 55%); }
+.fixture-footer { background: var(--causeway-props-surface); color: var(--causeway-props-muted); }
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css
new file mode 100644
index 00000000000..2f2f8e01810
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css
@@ -0,0 +1,18 @@
+:root { --fixture-shell-width: 82rem; --fixture-content-width: 76rem; --wa-font-family-body: system-ui, sans-serif; --wa-font-family-heading: system-ui, sans-serif; --wa-border-radius-scale: 1; --wa-space-scale: 1; }
+html[data-theme="dark"] { color-scheme: dark; }
+body { background: var(--wa-color-surface-lowered, Canvas); color: var(--wa-color-text-normal, CanvasText); font-family: var(--wa-font-family-body); }
+.fixture-header { background: var(--wa-color-brand-fill-loud, #075985); color: var(--wa-color-brand-on-loud, white); box-shadow: var(--wa-shadow-s); }
+.fixture-mark { background: var(--wa-color-neutral-fill-loud, #fff); color: var(--wa-color-brand-fill-loud, #075985); }
+.fixture-card, .fixture-status, .fixture-menu-panel { background: var(--wa-color-surface-default, Canvas); border: var(--wa-border-width-s) solid var(--wa-color-surface-border, GrayText); border-radius: var(--wa-border-radius-m); box-shadow: var(--wa-shadow-s); }
+.fixture-card wa-button, .fixture-actions wa-button { margin: .125rem; }
+.fixture-eyebrow, .fixture-property-label { color: var(--wa-color-text-quiet, GrayText); }
+.fixture-error { color: var(--wa-color-danger-fill-loud, #b91c1c); }
+.fixture-status[data-tone="success"] { border-inline-start: .35rem solid var(--wa-color-success-fill-loud, green); }
+.fixture-status[data-tone="warning"] { border-inline-start: .35rem solid var(--wa-color-warning-fill-loud, orange); }
+.fixture-status[data-tone="danger"] { border-inline-start: .35rem solid var(--wa-color-danger-fill-loud, red); }
+.fixture-table th { background: var(--wa-color-surface-raised, color-mix(in srgb, Canvas 90%, CanvasText)); }
+.fixture-table th, .fixture-table td { border-block-end: var(--wa-border-width-s) solid var(--wa-color-surface-border, GrayText); }
+.fixture-footer { background: var(--wa-color-surface-raised, Canvas); color: var(--wa-color-text-quiet, GrayText); }
+wa-dialog::part(dialog) { max-inline-size: min(38rem, calc(100vw - 2rem)); }
+wa-dropdown::part(panel) { min-inline-size: 17rem; }
+wa-button::part(base):focus-visible, wa-dropdown-item::part(base):focus-visible { outline: .2rem solid var(--wa-color-focus, Highlight); outline-offset: .1rem; }
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/capture.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/capture.mjs
new file mode 100644
index 00000000000..9c1209d9037
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/capture.mjs
@@ -0,0 +1,352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {spawn} from 'node:child_process';
+import {mkdirSync, writeFileSync} from 'node:fs';
+import {resolve} from 'node:path';
+import {fileURLToPath} from 'node:url';
+import {chromium} from 'playwright';
+
+const directory = fileURLToPath(new URL('.', import.meta.url));
+const evidenceDirectory = resolve(directory, '..');
+const screenshotDirectory = resolve(evidenceDirectory, 'screenshots');
+const resultDirectory = resolve(evidenceDirectory, 'results');
+const origin = process.env.HARNESS_ORIGIN ?? 'http://127.0.0.1:4173';
+const candidates = ['baseline', 'bootstrap', 'webawesome', 'openprops'];
+const cases = [
+  {name: 'desktop-light-menu', viewport: {width: 1440, height: 900}, theme: 'light', state: 'menu-open'},
+  {name: 'desktop-dark-prompt', viewport: {width: 1440, height: 900}, theme: 'dark', state: 'prompt'},
+  {name: 'narrow-light-nav', viewport: {width: 390, height: 844}, theme: 'light', state: 'responsive-nav', mobile: true},
+  {name: 'narrow-dark-all', viewport: {width: 390, height: 844}, theme: 'dark', state: 'all', mobile: true}
+];
+
+mkdirSync(screenshotDirectory, {recursive: true});
+mkdirSync(resultDirectory, {recursive: true});
+
+const server = await ensureServer();
+const executablePath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE || undefined;
+const browser = await chromium.launch({headless: true, executablePath});
+const evidence = {
+  generatedAt: new Date().toISOString(),
+  origin,
+  playwrightVersion: '1.61.0',
+  candidates: {},
+  notes: [
+    'Screenshots use identical fixture content and deterministic viewport, theme, and state parameters.',
+    'DOM audits are bounded structural checks and do not replace Lighthouse or manual assistive-technology review.',
+    'Performance values are local medians after one warm-up and exclude shared harness files from candidate transfer summaries.'
+  ]
+};
+
+try {
+  for (const candidate of candidates) {
+    const candidateEvidence = {screenshots: [], interactions: {}, media: {}, performance: null};
+    for (const testCase of cases) {
+      const context = await browser.newContext({
+        viewport: testCase.viewport,
+        colorScheme: testCase.theme,
+        reducedMotion: 'no-preference'
+      });
+      const page = await context.newPage();
+      const failures = collectFailures(page);
+      await installPerformanceObservers(page);
+      const url = prototypeUrl(candidate, testCase);
+      await page.goto(url, {waitUntil: 'networkidle'});
+      await waitForFixture(page);
+      const audit = await auditPage(page);
+      const screenshotName = `${candidate}-${testCase.name}.jpg`;
+      await page.screenshot({path: resolve(screenshotDirectory, screenshotName), fullPage: true, type: 'jpeg', quality: 82});
+      candidateEvidence.screenshots.push({case: testCase.name, file: `screenshots/${screenshotName}`, url, audit, failures});
+      await context.close();
+    }
+    candidateEvidence.interactions = await runInteractionJourneys(browser, candidate);
+    candidateEvidence.media = await runPreferenceJourney(browser, candidate);
+    candidateEvidence.performance = await measurePerformance(browser, candidate);
+    evidence.candidates[candidate] = candidateEvidence;
+  }
+} finally {
+  await browser.close();
+  server?.kill();
+}
+
+const jsonTarget = resolve(resultDirectory, 'browser-evidence.json');
+writeFileSync(jsonTarget, `${JSON.stringify(evidence, null, 2)}\n`);
+writeFileSync(resolve(resultDirectory, 'browser-evidence.md'), renderMarkdown(evidence));
+console.log(`Wrote ${jsonTarget}`);
+console.log(summary(evidence));
+
+async function ensureServer() {
+  try {
+    const response = await fetch(`${origin}/`);
+    if (response.ok || response.status === 302) return null;
+  } catch {
+    // Start the local server below.
+  }
+  const child = spawn(process.execPath, [resolve(directory, 'server.mjs')], {
+    cwd: directory,
+    env: {...process.env, PORT: new URL(origin).port || '4173', HOST: new URL(origin).hostname},
+    stdio: ['ignore', 'pipe', 'pipe']
+  });
+  const output = [];
+  child.stdout.on('data', chunk => output.push(String(chunk)));
+  child.stderr.on('data', chunk => output.push(String(chunk)));
+  for (let attempt = 0; attempt < 50; attempt += 1) {
+    await new Promise(resolvePromise => setTimeout(resolvePromise, 100));
+    try {
+      const response = await fetch(`${origin}/`);
+      if (response.ok || response.status === 302) return child;
+    } catch {
+      // Retry until the bounded timeout.
+    }
+  }
+  child.kill();
+  throw new Error(`Harness server did not start: ${output.join('')}`);
+}
+
+function prototypeUrl(candidate, options = {}) {
+  const url = new URL('/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html', origin);
+  url.searchParams.set('candidate', candidate);
+  url.searchParams.set('theme', options.theme ?? 'light');
+  url.searchParams.set('state', options.state ?? 'all');
+  url.searchParams.set('motion', options.motion ?? 'normal');
+  return url.href;
+}
+
+async function waitForFixture(page) {
+  await page.waitForFunction(() => document.documentElement.dataset.fixtureReady === 'true', null, {timeout: 20_000});
+}
+
+function collectFailures(page) {
+  const failures = [];
+  page.on('pageerror', error => failures.push({kind: 'page-error', message: String(error)}));
+  page.on('console', message => {
+    if (message.type() === 'error') failures.push({kind: 'console-error', message: message.text()});
+  });
+  page.on('requestfailed', request => failures.push({kind: 'request-failed', message: `${request.method()} ${request.url()} ${request.failure()?.errorText ?? ''}`}));
+  page.on('response', response => {
+    if (response.status() >= 400) failures.push({kind: 'http-error', message: `${response.status()} ${response.url()}`});
+  });
+  return failures;
+}
+
+async function installPerformanceObservers(page) {
+  await page.addInitScript(() => {
+    globalThis.__causewayLcp = null;
+    try {
+      new PerformanceObserver(list => {
+        const entries = list.getEntries();
+        globalThis.__causewayLcp = entries.at(-1)?.startTime ?? globalThis.__causewayLcp;
+      }).observe({type: 'largest-contentful-paint', buffered: true});
+    } catch {
+      // The result remains null when the browser does not expose LCP.
+    }
+  });
+}
+
+async function auditPage(page) {
+  return page.evaluate(() => {
+    const ids = [...document.querySelectorAll('[id]')].map(element => element.id);
+    const duplicateIds = [...new Set(ids.filter((id, index) => ids.indexOf(id) !== index))];
+    const unlabeledButtons = [...document.querySelectorAll('button, wa-button')]
+      .filter(element => !(element.getAttribute('aria-label') || element.textContent?.trim()))
+      .map(element => element.outerHTML.slice(0, 180));
+    const unlabeledInputs = [...document.querySelectorAll('input, select, textarea')]
+      .filter(element => !(element.labels?.length || element.getAttribute('aria-label') || element.getAttribute('aria-labelledby')))
+      .map(element => element.outerHTML.slice(0, 180));
+    const dialogsWithoutName = [...document.querySelectorAll('dialog, wa-dialog')]
+      .filter(element => !(element.getAttribute('label') || element.getAttribute('aria-label') || element.getAttribute('aria-labelledby')))
+      .map(element => element.outerHTML.slice(0, 180));
+    const active = document.activeElement;
+    const hiddenFocus = Boolean(active && active !== document.body && (active.hidden || active.closest('[hidden]')));
+    return {
+      title: document.title,
+      heading: document.querySelector('h1')?.textContent?.trim(),
+      duplicateIds,
+      unlabeledButtons,
+      unlabeledInputs,
+      dialogsWithoutName,
+      hiddenFocus,
+      horizontalOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
+      fixtureReady: document.documentElement.dataset.fixtureReady,
+      bodyBackground: getComputedStyle(document.body).backgroundColor,
+      bodyColor: getComputedStyle(document.body).color
+    };
+  });
+}
+
+async function runInteractionJourneys(browser, candidate) {
+  const journeys = {};
+  const context = await browser.newContext({viewport: {width: 1440, height: 900}, colorScheme: 'light'});
+  const page = await context.newPage();
+  const failures = collectFailures(page);
+  await page.goto(prototypeUrl(candidate), {waitUntil: 'networkidle'});
+  await waitForFixture(page);
+
+  const menuTrigger = candidate === 'webawesome' ? 'wa-dropdown[data-app-dropdown] wa-button' : '[data-menu-trigger]';
+  await page.locator(menuTrigger).click();
+  await page.waitForTimeout(100);
+  journeys.menuOpened = await menuState(page, candidate);
+  await page.keyboard.press('Escape');
+  await page.waitForTimeout(100);
+  journeys.menuEscape = await menuState(page, candidate);
+
+  await page.locator(menuTrigger).click();
+  await page.waitForTimeout(100);
+  const createAction = candidate === 'webawesome' ? 'wa-dropdown-item[data-menu-action="create"]' : '[data-menu-action="create"]';
+  await page.locator(createAction).click();
+  await page.waitForFunction(() => document.querySelector('[data-prompt]')?.open === true, null, {timeout: 10_000});
+  await page.waitForTimeout(candidate === 'webawesome' ? 500 : 100);
+  journeys.actionSelection = {
+    menu: await menuState(page, candidate),
+    prompt: await promptState(page),
+    focused: await focusedElement(page)
+  };
+  await page.keyboard.press('Escape');
+  await page.waitForTimeout(candidate === 'webawesome' ? 500 : 150);
+  journeys.promptEscape = {prompt: await promptState(page), focused: await focusedElement(page)};
+
+  const tabs = candidate === 'webawesome' ? page.locator('wa-tab') : page.locator('[role="tab"]');
+  if (await tabs.count() > 1) {
+    await tabs.nth(0).focus();
+    await page.keyboard.press('ArrowRight');
+    await page.waitForTimeout(100);
+    journeys.tabs = await page.evaluate(expected => {
+      if (expected === 'webawesome') {
+        return [...document.querySelectorAll('wa-tab')].map(tab => ({text: tab.textContent.trim(), active: tab.active ?? tab.getAttribute('aria-selected')}));
+      }
+      return [...document.querySelectorAll('[role="tab"]')].map(tab => ({text: tab.textContent.trim(), active: tab.getAttribute('aria-selected'), focused: document.activeElement === tab}));
+    }, candidate);
+  }
+  journeys.failures = failures;
+  await context.close();
+  return journeys;
+}
+
+async function menuState(page, candidate) {
+  return page.evaluate(expected => {
+    if (expected === 'webawesome') {
+      const dropdown = document.querySelector('[data-app-dropdown]');
+      const trigger = dropdown?.querySelector('wa-button');
+      return {open: Boolean(dropdown?.open), focused: document.activeElement === trigger, trigger: trigger?.textContent?.trim()};
+    }
+    const trigger = document.querySelector('[data-menu-trigger]');
+    const panel = document.querySelector('[data-menu-panel]');
+    const nativeOpen = panel?.matches?.(':popover-open') ?? false;
+    const nativePopoverSupported = expected === 'openprops' && 'showPopover' in HTMLElement.prototype;
+    return {
+      open: expected === 'openprops' ? (nativePopoverSupported ? nativeOpen : !panel?.hidden) : trigger?.getAttribute('aria-expanded') === 'true',
+      ariaExpanded: trigger?.getAttribute('aria-expanded'),
+      panelHidden: panel?.hidden,
+      panelDisplay: panel ? getComputedStyle(panel).display : null,
+      focused: document.activeElement === trigger
+    };
+  }, candidate);
+}
+
+async function promptState(page) {
+  return page.evaluate(() => {
+    const prompt = document.querySelector('[data-prompt]');
+    return {open: Boolean(prompt?.open), localName: prompt?.localName};
+  });
+}
+
+async function focusedElement(page) {
+  return page.evaluate(() => {
+    const active = document.activeElement;
+    return active ? {localName: active.localName, id: active.id || null, text: active.textContent?.trim().slice(0, 80) || null, testField: active.hasAttribute?.('data-prompt-field') ?? false} : null;
+  });
+}
+
+async function runPreferenceJourney(browser, candidate) {
+  const context = await browser.newContext({
+    viewport: {width: 390, height: 844},
+    colorScheme: 'dark',
+    reducedMotion: 'reduce',
+    forcedColors: 'active'
+  });
+  const page = await context.newPage();
+  const failures = collectFailures(page);
+  await page.goto(prototypeUrl(candidate, {theme: 'dark', state: 'responsive-nav', motion: 'reduce'}), {waitUntil: 'networkidle'});
+  await waitForFixture(page);
+  const result = await page.evaluate(() => ({
+    reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches,
+    forcedColors: matchMedia('(forced-colors: active)').matches,
+    dark: matchMedia('(prefers-color-scheme: dark)').matches,
+    horizontalOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
+    navVisible: getComputedStyle(document.querySelector('#fixture-navigation')).display !== 'none'
+  }));
+  result.failures = failures;
+  await context.close();
+  return result;
+}
+
+async function measurePerformance(browser, candidate) {
+  const context = await browser.newContext({viewport: {width: 1440, height: 900}, colorScheme: 'light'});
+  const page = await context.newPage();
+  const samples = [];
+  for (let run = 0; run < 6; run += 1) {
+    await installPerformanceObservers(page);
+    await page.goto(prototypeUrl(candidate), {waitUntil: 'networkidle'});
+    await waitForFixture(page);
+    const sample = await page.evaluate(expected => {
+      const navigation = performance.getEntriesByType('navigation')[0];
+      const paint = Object.fromEntries(performance.getEntriesByType('paint').map(entry => [entry.name, entry.startTime]));
+      const resources = performance.getEntriesByType('resource').map(entry => ({name: entry.name, transferSize: entry.transferSize, encodedBodySize: entry.encodedBodySize, duration: entry.duration}));
+      const marker = expected === 'baseline' ? '/viewers/webcomponents/'
+        : expected === 'bootstrap' ? '/node_modules/bootstrap/'
+          : expected === 'webawesome' ? '/node_modules/@awesome.me/webawesome/'
+            : '/node_modules/open-props/';
+      const owned = resources.filter(entry => entry.name.includes(marker) || entry.name.includes(`/candidates/${expected === 'webawesome' ? 'web-awesome' : expected === 'openprops' ? 'open-props' : expected}.css`));
+      return {
+        domContentLoaded: navigation?.domContentLoadedEventEnd ?? null,
+        load: navigation?.loadEventEnd ?? null,
+        firstContentfulPaint: paint['first-contentful-paint'] ?? null,
+        largestContentfulPaint: globalThis.__causewayLcp,
+        candidateRequestCount: owned.length,
+        candidateTransferBytes: owned.reduce((sum, entry) => sum + entry.transferSize, 0),
+        candidateEncodedBytes: owned.reduce((sum, entry) => sum + entry.encodedBodySize, 0),
+        candidateResourceDuration: owned.reduce((sum, entry) => sum + entry.duration, 0)
+      };
+    }, candidate);
+    if (run > 0) samples.push(sample);
+  }
+  await context.close();
+  return {samples, median: Object.fromEntries(Object.keys(samples[0] ?? {}).map(key => [key, median(samples.map(sample => sample[key]).filter(value => Number.isFinite(value)))]))};
+}
+
+function median(values) {
+  if (values.length === 0) return null;
+  const sorted = [...values].sort((left, right) => left - right);
+  const middle = Math.floor(sorted.length / 2);
+  return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
+}
+
+function renderMarkdown(result) {
+  const lines = ['# Browser evidence summary', '', `Generated: ${result.generatedAt}`, ''];
+  for (const [candidate, data] of Object.entries(result.candidates)) {
+    const screenshotFailures = data.screenshots.flatMap(item => item.failures);
+    lines.push(`## ${candidate}`, '');
+    lines.push(`- Screenshots: ${data.screenshots.length}.`);
+    lines.push(`- Browser failures: ${screenshotFailures.length + data.interactions.failures.length + data.media.failures.length}.`);
+    lines.push(`- Horizontal overflow: ${data.screenshots.map(item => item.audit.horizontalOverflow).join(', ')} pixels.`);
+    lines.push(`- Menu opened: ${JSON.stringify(data.interactions.menuOpened)}.`);
+    lines.push(`- Menu after Escape: ${JSON.stringify(data.interactions.menuEscape)}.`);
+    lines.push(`- Action selection: ${JSON.stringify(data.interactions.actionSelection)}.`);
+    lines.push(`- Prompt after Escape: ${JSON.stringify(data.interactions.promptEscape)}.`);
+    lines.push(`- Reduced motion / forced colors: ${data.media.reducedMotion} / ${data.media.forcedColors}.`);
+    lines.push(`- Median performance: ${JSON.stringify(data.performance.median)}.`, '');
+  }
+  return `${lines.join('\n')}\n`;
+}
+
+function summary(result) {
+  return Object.entries(result.candidates).map(([candidate, data]) => {
+    const failures = data.screenshots.flatMap(item => item.failures).length + data.interactions.failures.length + data.media.failures.length;
+    return `${candidate}: screenshots=${data.screenshots.length}, failures=${failures}, overflow=${Math.max(...data.screenshots.map(item => item.audit.horizontalOverflow))}`;
+  }).join('\n');
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/fixture.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/fixture.mjs
new file mode 100644
index 00000000000..58a3e1d6932
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/fixture.mjs
@@ -0,0 +1,448 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+const harnessBase = '/openspec/changes/analyze-web-component-theming-kit/evidence/harness';
+const parameters = new URLSearchParams(globalThis.location.search);
+const supportedCandidates = new Set(['baseline', 'bootstrap', 'webawesome', 'openprops']);
+const candidate = supportedCandidates.has(parameters.get('candidate')) ? parameters.get('candidate') : 'baseline';
+const theme = parameters.get('theme') === 'dark' ? 'dark' : 'light';
+const requestedState = parameters.get('state') ?? 'all';
+const motion = parameters.get('motion') === 'reduce' ? 'reduce' : 'normal';
+let promptInvoker = null;
+
+document.documentElement.dataset.candidate = candidate;
+document.documentElement.dataset.theme = theme;
+document.documentElement.dataset.motion = motion;
+document.documentElement.style.colorScheme = theme;
+if (candidate === 'bootstrap') {
+  document.documentElement.dataset.bsTheme = theme;
+}
+if (candidate === 'webawesome') {
+  document.documentElement.classList.add(theme === 'dark' ? 'wa-dark' : 'wa-light');
+}
+
+await loadCandidateAssets();
+
+const root = document.querySelector('#fixture-root');
+root.replaceChildren();
+root.insertAdjacentHTML('beforeend', renderFixture());
+setupInteractions();
+await awaitCandidateDefinitions();
+activateRequestedState();
+
+const badge = document.createElement('div');
+badge.className = 'prototype-badge';
+badge.textContent = `${candidate} · ${theme} · ${requestedState}`;
+document.body.append(badge);
+
+await document.fonts?.ready;
+window.__causewayFixture = Object.freeze({candidate, theme, requestedState, motion, ready: true});
+document.documentElement.dataset.fixtureReady = 'true';
+document.dispatchEvent(new CustomEvent('causeway-fixture-ready', {detail: window.__causewayFixture}));
+
+async function loadCandidateAssets() {
+  const assets = {
+    baseline: [
+      '/viewers/webcomponents/foundation/src/theme.css',
+      '/viewers/webcomponents/foundation/src/component-styles.css',
+      '/viewers/webcomponents/htmx/src/main/resources/META-INF/resources/causeway-htmx/causeway-htmx.css',
+      `${harnessBase}/candidates/baseline.css`
+    ],
+    bootstrap: [
+      `${harnessBase}/node_modules/bootstrap/dist/css/bootstrap.min.css`,
+      `${harnessBase}/candidates/bootstrap.css`
+    ],
+    webawesome: [
+      `${harnessBase}/node_modules/@awesome.me/webawesome/dist-cdn/styles/webawesome.css`,
+      `${harnessBase}/candidates/web-awesome.css`
+    ],
+    openprops: [
+      `${harnessBase}/node_modules/open-props/open-props.min.css`,
+      `${harnessBase}/node_modules/open-props/normalize.min.css`,
+      `${harnessBase}/node_modules/open-props/buttons.min.css`,
+      `${harnessBase}/candidates/open-props.css`
+    ]
+  };
+  await Promise.all(assets[candidate].map(loadStylesheet));
+  if (candidate === 'webawesome') {
+    const distributionPath = `${harnessBase}/node_modules/@awesome.me/webawesome/dist-cdn`;
+    const {setBasePath} = await import(`${distributionPath}/utilities/base-path.js`);
+    setBasePath(distributionPath);
+    await import(`${distributionPath}/webawesome.loader.js`);
+  }
+}
+
+function loadStylesheet(href) {
+  return new Promise((resolve, reject) => {
+    const link = document.createElement('link');
+    link.rel = 'stylesheet';
+    link.href = href;
+    link.addEventListener('load', resolve, {once: true});
+    link.addEventListener('error', () => reject(new Error(`Unable to load ${href}`)), {once: true});
+    document.head.append(link);
+  });
+}
+
+function renderFixture() {
+  return `<div class="fixture-app">
+    <header class="fixture-header">
+      <div class="fixture-navbar">
+        <a class="fixture-brand" href="#fixture-main" aria-label="Pet Clinic home">
+          <span class="fixture-mark" aria-hidden="true">C</span>
+          <span>Pet Clinic</span>
+        </a>
+        ${control('Application menu', {kind: 'secondary', attributes: 'class="fixture-nav-toggle" data-nav-trigger aria-expanded="false" aria-controls="fixture-navigation"'})}
+        <nav id="fixture-navigation" class="fixture-nav" data-collapsed="true" aria-label="Application navigation">
+          <causeway-menubars data-prototype-host class="fixture-menus">
+            ${renderMenus()}
+          </causeway-menubars>
+        </nav>
+      </div>
+    </header>
+
+    <main id="fixture-main" class="fixture-main" tabindex="-1">
+      <div class="fixture-heading-row">
+        <div>
+          <p class="fixture-eyebrow">Pet owner</p>
+          <h1>Mary Smith and a deliberately long contextual title</h1>
+          <p>Reference object <code>petclinic.PetOwner / s_owner-mary</code></p>
+        </div>
+        <div class="fixture-actions" aria-label="Object actions">
+          ${control('Update name', {kind: 'secondary', attributes: 'data-object-action="update"'})}
+          ${control('Book visit', {kind: 'primary', attributes: 'data-object-action="book"'})}
+          ${control('Delete', {kind: 'danger', attributes: 'data-object-action="delete"'})}
+        </div>
+      </div>
+
+      <aside class="fixture-shell-result fixture-status" data-tone="success" aria-live="polite">
+        <strong>Action completed.</strong>
+        The fixture retains a concise semantic result without replacing the route.
+      </aside>
+
+      <div class="fixture-page-grid">
+        <causeway-object data-prototype-host class="fixture-card">
+          <causeway-object-header data-prototype-host>
+            <h2>Owner details</h2>
+            <p class="fixture-eyebrow">Ready · editable · fixture state</p>
+          </causeway-object-header>
+          ${renderTabs()}
+        </causeway-object>
+
+        <aside class="fixture-card" aria-labelledby="associated-actions-heading">
+          <h2 id="associated-actions-heading">Associated actions</h2>
+          <div class="fixture-actions">
+            ${control('Add pet', {kind: 'primary'})}
+            ${control('Remove pet', {kind: 'secondary'})}
+            ${control('Unavailable action', {kind: 'secondary', attributes: 'disabled aria-describedby="disabled-action-reason"'})}
+          </div>
+          <p id="disabled-action-reason" class="fixture-eyebrow">Available only when a pet is selected.</p>
+        </aside>
+      </div>
+
+      <causeway-collection data-prototype-host class="fixture-card" aria-labelledby="collection-heading">
+        <h2 id="collection-heading">Visits and related records</h2>
+        <div class="fixture-table-region" tabindex="0" aria-label="Scrollable visits table">
+          ${renderTable()}
+        </div>
+      </causeway-collection>
+
+      <section class="fixture-card" aria-labelledby="status-heading">
+        <h2 id="status-heading">State presentations</h2>
+        <div class="fixture-status-grid">
+          <div class="fixture-status" role="status"><strong>Loading</strong><br>${candidate === 'webawesome' ? '<wa-spinner></wa-spinner>' : 'Loading object data…'}</div>
+          <div class="fixture-status" data-tone="success"><strong>Ready</strong><br>All requested members are available.</div>
+          <div class="fixture-status" data-tone="warning"><strong>Partial information</strong><br>One optional collection could not be loaded.</div>
+          <div class="fixture-status" data-tone="danger" role="alert"><strong>Validation error</strong><br>Correct the highlighted fields and retry.</div>
+          <div class="fixture-status"><strong>Empty</strong><br>No matching records were found.</div>
+          <div class="fixture-status" data-tone="danger" role="alert"><strong>Page unavailable</strong><br>The request could not be completed safely.</div>
+        </div>
+      </section>
+    </main>
+
+    <footer class="fixture-footer">Powered by Apache Causeway · analysis fixture only</footer>
+    ${renderPrompt()}
+  </div>`;
+}
+
+function renderMenus() {
+  if (candidate === 'webawesome') {
+    return `<wa-dropdown data-app-dropdown>
+      <wa-button appearance="filled" slot="trigger" with-caret>Pet Owners</wa-button>
+      <wa-dropdown-item value="create" data-menu-action="create">Create</wa-dropdown-item>
+      <wa-dropdown-item value="find">Find By Name</wa-dropdown-item>
+      <wa-dropdown-item value="long">A deliberately long application action label</wa-dropdown-item>
+      <wa-dropdown-item value="disabled" disabled>Unavailable action</wa-dropdown-item>
+    </wa-dropdown>
+    <wa-dropdown>
+      <wa-button appearance="plain" slot="trigger" with-caret>Visits</wa-button>
+      <wa-dropdown-item value="upcoming">List Upcoming</wa-dropdown-item>
+    </wa-dropdown>
+    <wa-dropdown>
+      <wa-button appearance="plain" slot="trigger" with-caret>System</wa-button>
+      <wa-dropdown-item value="about">About</wa-dropdown-item>
+    </wa-dropdown>`;
+  }
+  const popover = candidate === 'openprops' ? 'popover="auto"' : 'hidden';
+  const target = candidate === 'openprops' ? 'popovertarget="pet-owner-menu"' : '';
+  return `<div class="fixture-menu">
+      ${control('Pet Owners', {kind: 'secondary', attributes: `data-menu-trigger ${target} aria-expanded="false" aria-controls="pet-owner-menu"`})}
+      <div id="pet-owner-menu" class="fixture-menu-panel" data-menu-panel ${popover} aria-label="Pet Owners actions">
+        ${control('Create', {kind: 'primary', attributes: 'data-menu-action="create"'})}
+        ${control('Find By Name', {kind: 'secondary', attributes: 'data-menu-action="find"'})}
+        ${control('A deliberately long application action label', {kind: 'secondary', attributes: 'data-menu-action="long"'})}
+        ${control('Unavailable action', {kind: 'secondary', attributes: 'disabled aria-describedby="menu-disabled-reason"'})}
+        <span id="menu-disabled-reason" class="fixture-eyebrow">Requires administrator access.</span>
+      </div>
+    </div>
+    <div class="fixture-menu">${control('Visits', {kind: 'secondary', attributes: 'aria-expanded="false"'})}</div>
+    <div class="fixture-menu">${control('System', {kind: 'secondary', attributes: 'aria-expanded="false"'})}</div>`;
+}
+
+function renderTabs() {
+  if (candidate === 'webawesome') {
+    return `<wa-tab-group>
+      <wa-tab slot="nav" panel="details">Details</wa-tab>
+      <wa-tab slot="nav" panel="notes">Notes and preferences</wa-tab>
+      <wa-tab-panel name="details">${renderProperties()}</wa-tab-panel>
+      <wa-tab-panel name="notes"><p class="fixture-long-value">This long unbroken value checks containment: abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789.</p></wa-tab-panel>
+    </wa-tab-group>`;
+  }
+  return `<div class="fixture-tabs">
+    <div role="tablist" aria-label="Owner sections">
+      ${control('Details', {kind: 'secondary', attributes: 'role="tab" id="details-tab" aria-selected="true" aria-controls="details-panel" data-tab="details-panel"'})}
+      ${control('Notes and preferences', {kind: 'secondary', attributes: 'role="tab" id="notes-tab" aria-selected="false" aria-controls="notes-panel" data-tab="notes-panel"'})}
+    </div>
+    <div id="details-panel" role="tabpanel" aria-labelledby="details-tab">${renderProperties()}</div>
+    <div id="notes-panel" role="tabpanel" aria-labelledby="notes-tab" hidden><p class="fixture-long-value">This long unbroken value checks containment: abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789.</p></div>
+  </div>`;
+}
+
+function renderProperties() {
+  return `<div class="fixture-properties">
+    <span class="fixture-property-label">Name</span><span>Mary Smith</span>
+    <span class="fixture-property-label">Known as</span><span>Mary</span>
+    <span class="fixture-property-label">Telephone number with a long label</span><span>020 7946 0312</span>
+    <span class="fixture-property-label">Email</span><a href="mailto:mary@example.test">mary@example.test</a>
+    <span class="fixture-property-label">Notes</span><span>Prefers morning appointments and requires step-free access.</span>
+  </div>`;
+}
+
+function renderTable() {
+  const tableClass = candidate === 'bootstrap' ? 'fixture-table table table-striped table-hover align-middle' : 'fixture-table';
+  return `<table class="${tableClass}">
+    <thead><tr><th>Pet</th><th>Visit date</th><th>Reason</th><th>Status</th><th>Veterinarian</th><th>Reference</th></tr></thead>
+    <tbody>
+      <tr><td><a href="#basil">Basil · dog</a></td><td>2026-08-24 09:30</td><td>Routine check-up</td><td>Confirmed</td><td>Dr Green</td><td>visit-2026-000031</td></tr>
+      <tr><td><a href="#parsley">Parsley · cat</a></td><td>2026-09-03 15:15</td><td>A deliberately long follow-up description that tests wrapping</td><td>Awaiting notes</td><td>Dr Hernández-Smith</td><td>visit-2026-000032</td></tr>
+    </tbody>
+  </table>`;
+}
+
+function renderPrompt() {
+  const fields = candidate === 'webawesome'
+    ? `<wa-input label="Name" required value="Invalid % name" data-prompt-field></wa-input>
+       <wa-select label="Preferred species" value="DOG"><wa-option value="DOG">Dog</wa-option><wa-option value="CAT">Cat</wa-option></wa-select>
+       <wa-textarea label="Notes" rows="4">Requires a quiet waiting area.</wa-textarea>`
+    : `<div class="fixture-field"><label for="owner-name">Name</label><input id="owner-name" required value="Invalid % name" aria-invalid="true" aria-describedby="owner-name-error" data-prompt-field><span id="owner-name-error" class="fixture-error" role="alert">Name cannot contain the % character.</span></div>
+       <div class="fixture-field-row"><div class="fixture-field"><label for="species">Preferred species</label><select id="species"><option>Dog</option><option>Cat</option></select></div><div class="fixture-field"><label for="visit-date">First visit</label><input id="visit-date" type="date" value="2026-08-24"></div></div>
+       <div class="fixture-field"><label for="owner-notes">Notes</label><textarea id="owner-notes" rows="4">Requires a quiet waiting area.</textarea></div>`;
+  const content = `<form class="fixture-dialog-form" data-prompt-form>
+      <p>Register a new pet owner using representative controls and validation.</p>
+      ${fields}
+      <div class="fixture-dialog-actions">
+        ${control('Cancel', {kind: 'secondary', attributes: 'type="button" data-prompt-cancel'})}
+        ${control('Create owner', {kind: 'primary', attributes: 'type="submit"'})}
+      </div>
+    </form>`;
+  if (candidate === 'webawesome') {
+    return `<causeway-action-prompt data-prototype-host><wa-dialog label="Create pet owner" data-prompt>${content}</wa-dialog></causeway-action-prompt>`;
+  }
+  return `<causeway-action-prompt data-prototype-host><dialog data-prompt aria-labelledby="prompt-heading"><h2 id="prompt-heading">Create pet owner</h2>${content}</dialog></causeway-action-prompt>`;
+}
+
+function control(label, {kind = 'secondary', attributes = ''} = {}) {
+  if (candidate === 'webawesome') {
+    const appearance = kind === 'primary' || kind === 'danger' ? 'filled' : 'outlined';
+    const variant = kind === 'danger' ? 'danger' : kind === 'primary' ? 'brand' : 'neutral';
+    return `<wa-button appearance="${appearance}" variant="${variant}" ${attributes}>${label}</wa-button>`;
+  }
+  const classes = candidate === 'bootstrap'
+    ? `btn ${kind === 'primary' ? 'btn-primary' : kind === 'danger' ? 'btn-danger' : 'btn-outline-secondary'}`
+    : 'fixture-button';
+  return `<button class="${classes}" data-kind="${kind}" type="button" ${attributes}>${label}</button>`;
+}
+
+function setupInteractions() {
+  const navTrigger = document.querySelector('[data-nav-trigger]');
+  const navigation = document.querySelector('#fixture-navigation');
+  navTrigger?.addEventListener('click', () => {
+    const expanded = navTrigger.getAttribute('aria-expanded') !== 'true';
+    navTrigger.setAttribute('aria-expanded', String(expanded));
+    navigation.dataset.collapsed = String(!expanded);
+  });
+
+  if (candidate !== 'webawesome' && candidate !== 'openprops') {
+    const trigger = document.querySelector('[data-menu-trigger]');
+    const panel = document.querySelector('[data-menu-panel]');
+    trigger?.addEventListener('click', () => toggleDisclosure(trigger, panel));
+    document.addEventListener('click', event => {
+      if (trigger?.getAttribute('aria-expanded') === 'true' && !trigger.parentElement.contains(event.target)) {
+        closeDisclosure(trigger, panel, false);
+      }
+    });
+  }
+
+  if (candidate === 'openprops') {
+    const trigger = document.querySelector('[data-menu-trigger]');
+    const panel = document.querySelector('[data-menu-panel]');
+    if (!('showPopover' in HTMLElement.prototype)) {
+      panel.removeAttribute('popover');
+      panel.hidden = true;
+      trigger.removeAttribute('popovertarget');
+      trigger.addEventListener('click', () => toggleDisclosure(trigger, panel));
+      document.documentElement.dataset.popoverFallback = 'true';
+    } else {
+      trigger.addEventListener('click', () => positionPopover(trigger, panel), {capture: true});
+      panel.addEventListener('toggle', event => trigger.setAttribute('aria-expanded', String(event.newState === 'open')));
+    }
+  }
+
+  document.addEventListener('click', event => {
+    const action = event.target.closest?.('[data-menu-action="create"]');
+    if (!action) return;
+    promptInvoker = document.querySelector('[data-menu-trigger], [data-app-dropdown] wa-button');
+    dismissApplicationMenu();
+    openPrompt();
+  });
+
+  document.addEventListener('keydown', event => {
+    if (event.key !== 'Escape') return;
+    const trigger = document.querySelector('[data-menu-trigger]');
+    const panel = document.querySelector('[data-menu-panel]');
+    if (trigger?.getAttribute('aria-expanded') === 'true' && candidate !== 'openprops') {
+      event.preventDefault();
+      closeDisclosure(trigger, panel, true);
+    }
+  });
+
+  const prompt = document.querySelector('[data-prompt]');
+  prompt?.addEventListener(candidate === 'webawesome' ? 'wa-after-hide' : 'close', () => promptInvoker?.focus());
+  document.querySelector('[data-prompt-cancel]')?.addEventListener('click', closePrompt);
+  document.querySelector('[data-prompt-form]')?.addEventListener('submit', event => {
+    event.preventDefault();
+    const invalid = document.querySelector('[aria-invalid="true"], wa-input[required]');
+    invalid?.focus?.();
+  });
+
+  for (const tab of document.querySelectorAll('[role="tab"][data-tab]')) {
+    tab.addEventListener('click', () => selectTab(tab));
+    tab.addEventListener('keydown', event => {
+      if (!['ArrowLeft', 'ArrowRight'].includes(event.key)) return;
+      const tabs = [...document.querySelectorAll('[role="tab"][data-tab]')];
+      const direction = event.key === 'ArrowRight' ? 1 : -1;
+      const next = tabs[(tabs.indexOf(tab) + direction + tabs.length) % tabs.length];
+      event.preventDefault();
+      selectTab(next);
+      next.focus();
+    });
+  }
+}
+
+function toggleDisclosure(trigger, panel) {
+  const expanded = trigger.getAttribute('aria-expanded') !== 'true';
+  trigger.setAttribute('aria-expanded', String(expanded));
+  panel.hidden = !expanded;
+}
+
+function closeDisclosure(trigger, panel, focus) {
+  trigger?.setAttribute('aria-expanded', 'false');
+  if (panel) panel.hidden = true;
+  if (focus) trigger?.focus();
+}
+
+function dismissApplicationMenu() {
+  if (candidate === 'webawesome') {
+    const dropdown = document.querySelector('[data-app-dropdown]');
+    if (dropdown) dropdown.open = false;
+    return;
+  }
+  const trigger = document.querySelector('[data-menu-trigger]');
+  const panel = document.querySelector('[data-menu-panel]');
+  if (candidate === 'openprops' && panel?.matches(':popover-open')) {
+    panel.hidePopover();
+    trigger?.setAttribute('aria-expanded', 'false');
+  } else {
+    closeDisclosure(trigger, panel, false);
+  }
+}
+
+function positionPopover(trigger, panel) {
+  const rect = trigger.getBoundingClientRect();
+  panel.style.insetInlineStart = `${Math.max(8, rect.left)}px`;
+  panel.style.insetBlockStart = `${rect.bottom + 4}px`;
+}
+
+function openPrompt() {
+  const prompt = document.querySelector('[data-prompt]');
+  if (!prompt) return;
+  if (candidate === 'webawesome') {
+    prompt.addEventListener('wa-after-show', () => prompt.querySelector('[data-prompt-field]')?.focus(), {once: true});
+    prompt.open = true;
+  } else if (!prompt.open) {
+    prompt.showModal();
+    prompt.querySelector('[data-prompt-field]')?.focus();
+  }
+}
+
+function closePrompt() {
+  const prompt = document.querySelector('[data-prompt]');
+  if (!prompt) return;
+  if (candidate === 'webawesome') prompt.open = false;
+  else if (prompt.open) prompt.close();
+  promptInvoker?.focus();
+}
+
+function selectTab(selected) {
+  for (const tab of document.querySelectorAll('[role="tab"][data-tab]')) {
+    const active = tab === selected;
+    tab.setAttribute('aria-selected', String(active));
+    document.getElementById(tab.dataset.tab).hidden = !active;
+  }
+}
+
+async function awaitCandidateDefinitions() {
+  if (candidate !== 'webawesome') return;
+  await Promise.all(['wa-button', 'wa-dialog', 'wa-dropdown', 'wa-dropdown-item', 'wa-input', 'wa-option', 'wa-select', 'wa-spinner', 'wa-tab', 'wa-tab-group', 'wa-tab-panel', 'wa-textarea']
+    .map(name => customElements.whenDefined(name)));
+}
+
+function activateRequestedState() {
+  if (requestedState === 'menu-open' || requestedState === 'responsive-nav') {
+    const navTrigger = document.querySelector('[data-nav-trigger]');
+    const navigation = document.querySelector('#fixture-navigation');
+    navTrigger?.setAttribute('aria-expanded', 'true');
+    if (navigation) navigation.dataset.collapsed = 'false';
+    if (candidate === 'webawesome') {
+      const dropdown = document.querySelector('[data-app-dropdown]');
+      if (dropdown) dropdown.open = true;
+    } else if (candidate === 'openprops') {
+      const trigger = document.querySelector('[data-menu-trigger]');
+      const panel = document.querySelector('[data-menu-panel]');
+      if (panel && 'showPopover' in panel) {
+        positionPopover(trigger, panel);
+        panel.showPopover();
+        trigger.setAttribute('aria-expanded', 'true');
+      } else {
+        toggleDisclosure(trigger, panel);
+      }
+    } else {
+      toggleDisclosure(document.querySelector('[data-menu-trigger]'), document.querySelector('[data-menu-panel]'));
+    }
+  }
+  if (requestedState === 'prompt') openPrompt();
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.css b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.css
new file mode 100644
index 00000000000..eca46374524
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.css
@@ -0,0 +1 @@
+@layer wa-native,wa-base,wa-utilities,wa-color-palette,wa-color-variant,wa-theme,wa-theme-dimension,wa-theme-overrides,wa-base,wa-utilities;@layer wa-native{html{box-sizing:border-box;margin:0;padding:0;tab-size:4;background-color:var(--wa-color-surface-default)}*,*:before,*:after{box-sizing:inherit}body{min-height:100vh;margin:0;padding:0;color:var(--wa-color-text-normal);font-family:var(--wa-font-family-body);font-size:var(--wa-font-size-m);font-weight:var(--wa-font-weight-body);line-h [...]
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.js b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.js
new file mode 100644
index 00000000000..3b9221031a5
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.js
@@ -0,0 +1,3944 @@
+var Kt=()=>({checkValidity(t){let e=t.input,o={message:"",isValid:!0,invalidKeys:[]};if(!e)return o;let i=!0;if("checkValidity"in e&&(i=e.checkValidity()),i)return o;if(o.isValid=!1,"validationMessage"in e&&(o.message=e.validationMessage),!("validity"in e))return o.invalidKeys.push("customError"),o;for(let r in e.validity){if(r==="valid")continue;let a=r;e.validity[a]&&o.invalidKeys.push(a)}return o}});var Ee=class extends Event{constructor(){super("wa-invalid",{bubbles:!0,cancelable:!1, [...]
+\f\r]`,he=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Go=/-->/g,Qo=/>/g,Tt=RegExp(`>|${co}(?:([^\\s"'>=/]+)(${co}*=${co}*(?:[^ 	
+\f\r"'\`<>=]|("|')|))|$)`,"g"),Zo=/'/g,Jo=/"/g,oi=/^(?:script|style|textarea|title)$/i,fo=t=>(e,...o)=>({_$litType$:t,strings:e,values:o}),g=fo(1),ii=fo(2),ri=fo(3),W=Symbol.for("lit-noChange"),T=Symbol.for("lit-nothing"),ti=new WeakMap,Pt=Rt.createTreeWalker(Rt,129);function ai(t,e){if(!mo(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return Yo!==void 0?Yo.createHTML(e):e}var ni=(t,e)=>{let o=t.length-1,i=[],r,a=e===2?"<svg>":e===3?"<math>":"",n=he;for(let c [...]
+  :host {
+    box-sizing: border-box;
+  }
+
+  :host *,
+  :host *::before,
+  :host *::after {
+    box-sizing: inherit;
+  }
+
+  [hidden],
+  :host([hidden]) {
+    display: none !important;
+  }
+`,Zr=/;\s+$/;function Jr(t){return t.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}function ci(t){let{property:e,value:o,element:i}=t;if(o){let r=i.getAttribute("style")||"";r&&(r.match(Zr)||(r+=";"),r+=" ");let a=`${e}: ${o}`;return r.includes(a)?void 0:`${r}${a};`}return null}var Fe,P=class extends At{constructor(){super(),Vo(this,Fe,!1),this.initialReflectedProperties=new Map,this.didSSR=!!this.shadowRoot,this.customStates={set:(e,o)=>{if(this.internals?.states)try{o?this.internals.state [...]
+  @layer wa-component {
+    :host {
+      display: inline-block;
+
+      /* Workaround because Chrome doesn't like :host(:has()) below
+       * https://issues.chromium.org/issues/40062355
+       * Firefox doesn't like this nested rule, so both are needed */
+      &:has(wa-badge) {
+        position: relative;
+      }
+    }
+
+    /* Apply relative positioning only when needed to position wa-badge
+     * This avoids creating a new stacking context for every button */
+    :host(:has(wa-badge)) {
+      position: relative;
+    }
+  }
+
+  .button {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    text-decoration: none;
+    user-select: none;
+    -webkit-user-select: none;
+    white-space: nowrap;
+    vertical-align: middle;
+    transition-property: background, border, box-shadow, color, opacity, transform;
+    transition-duration: var(--wa-transition-fast);
+    transition-timing-function: var(--wa-transition-easing);
+    transform-origin: center;
+    cursor: pointer;
+    padding: 0 var(--wa-form-control-padding-inline);
+    font-family: inherit;
+    font-size: inherit;
+    font-weight: var(--wa-font-weight-action);
+    height: var(--wa-form-control-height);
+    width: 100%;
+
+    background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
+
+    border-color: transparent;
+    color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
+    border-start-start-radius: var(--_button-start-start-radius, var(--wa-form-control-border-radius));
+    border-start-end-radius: var(--_button-start-end-radius, var(--wa-form-control-border-radius));
+    border-end-start-radius: var(--_button-end-start-radius, var(--wa-form-control-border-radius));
+    border-end-end-radius: var(--_button-end-end-radius, var(--wa-form-control-border-radius));
+    border-style: var(--wa-form-control-border-style);
+    border-width: var(--wa-form-control-border-width);
+  }
+
+  /* Hover and active transforms */
+  .button:not(.disabled):not(.loading) {
+    @media (hover: hover) {
+      &:hover {
+        transform: var(--wa-button-transform-hover);
+      }
+    }
+    &:active {
+      transform: var(--wa-button-transform-active);
+    }
+
+    @media (prefers-reduced-motion: reduce) {
+      &:hover,
+      &:active {
+        transform: none;
+      }
+    }
+  }
+
+  /* Appearance modifiers */
+  :host([appearance='plain']) {
+    /* Indentation overrides for grouping */
+    margin-inline-start: var(--_button-horizontal-indent);
+    margin-block-start: var(--_button-vertical-indent);
+
+    .button {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: transparent;
+      border-color: transparent;
+    }
+    @media (hover: hover) {
+      .button:not(.disabled):not(.loading):hover {
+        color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+        background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+      }
+    }
+    .button:not(.disabled):not(.loading):active {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: color-mix(
+        in oklab,
+        var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet)),
+        var(--wa-color-mix-active)
+      );
+    }
+  }
+
+  :host([appearance='outlined']) {
+    /* Indentation overrides for grouping outlined */
+    margin-inline-start: var(--_button-horizontal-indent-outlined);
+    margin-block-start: var(--_button-vertical-indent-outlined);
+
+    .button {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: transparent;
+      border-color: var(--wa-color-border-loud, var(--wa-color-neutral-border-loud));
+    }
+    @media (hover: hover) {
+      .button:not(.disabled):not(.loading):hover {
+        color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+        background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+      }
+    }
+    .button:not(.disabled):not(.loading):active {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: color-mix(
+        in oklab,
+        var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet)),
+        var(--wa-color-mix-active)
+      );
+    }
+  }
+
+  :host([appearance='filled']) {
+    /* Indentation overrides for grouping */
+    margin-inline-start: var(--_button-horizontal-indent);
+    margin-block-start: var(--_button-vertical-indent);
+
+    .button {
+      color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+      background-color: var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal));
+      border-color: transparent;
+    }
+    @media (hover: hover) {
+      .button:not(.disabled):not(.loading):hover {
+        color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+        background-color: color-mix(
+          in oklab,
+          var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+          var(--wa-color-mix-hover)
+        );
+      }
+    }
+    .button:not(.disabled):not(.loading):active {
+      color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+      background-color: color-mix(
+        in oklab,
+        var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+        var(--wa-color-mix-active)
+      );
+    }
+  }
+
+  :host([appearance='filled-outlined']) {
+    /* Indentation overrides for grouping outlined */
+    margin-inline-start: var(--_button-horizontal-indent-outlined);
+    margin-block-start: var(--_button-vertical-indent-outlined);
+
+    .button {
+      color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+      background-color: var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal));
+      border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
+    }
+    @media (hover: hover) {
+      .button:not(.disabled):not(.loading):hover {
+        color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+        background-color: color-mix(
+          in oklab,
+          var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+          var(--wa-color-mix-hover)
+        );
+      }
+    }
+    .button:not(.disabled):not(.loading):active {
+      color: var(--wa-color-on-normal, var(--wa-color-neutral-on-normal));
+      background-color: color-mix(
+        in oklab,
+        var(--wa-color-fill-normal, var(--wa-color-neutral-fill-normal)),
+        var(--wa-color-mix-active)
+      );
+    }
+  }
+
+  :host([appearance='accent']) {
+    /* Indentation overrides for grouping */
+    margin-inline-start: var(--_button-horizontal-indent);
+    margin-block-start: var(--_button-vertical-indent);
+
+    .button {
+      color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
+      background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
+      border-color: transparent;
+    }
+    @media (hover: hover) {
+      .button:not(.disabled):not(.loading):hover {
+        background-color: color-mix(
+          in oklab,
+          var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud)),
+          var(--wa-color-mix-hover)
+        );
+      }
+    }
+    .button:not(.disabled):not(.loading):active {
+      background-color: color-mix(
+        in oklab,
+        var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud)),
+        var(--wa-color-mix-active)
+      );
+    }
+  }
+
+  /* Focus states */
+  .button:focus {
+    outline: none;
+  }
+
+  .button:focus-visible {
+    outline: var(--wa-focus-ring);
+    outline-offset: var(--wa-focus-ring-offset);
+  }
+
+  /* Disabled state */
+  :host([disabled]) {
+    opacity: 0.5;
+    cursor: not-allowed;
+
+    /* When disabled, prevent mouse events from bubbling up from children */
+    .button {
+      pointer-events: none;
+    }
+  }
+
+  /* Keep it last so Safari doesn't stop parsing this block */
+  .button::-moz-focus-inner {
+    border: 0;
+  }
+
+  /* Icon buttons */
+  .button.is-icon-button {
+    outline-offset: 2px;
+    width: var(--wa-form-control-height);
+    aspect-ratio: 1;
+  }
+
+  /* Icon buttons with a caret need to grow to fit both the icon and the caret */
+  .button.is-icon-button.caret {
+    width: auto;
+    aspect-ratio: auto;
+    min-width: var(--wa-form-control-height);
+  }
+
+  /* Pill modifier */
+  :host([pill]) .button {
+    border-start-start-radius: var(--_button-start-start-radius, var(--wa-border-radius-pill));
+    border-start-end-radius: var(--_button-start-end-radius, var(--wa-border-radius-pill));
+    border-end-start-radius: var(--_button-end-start-radius, var(--wa-border-radius-pill));
+    border-end-end-radius: var(--_button-end-end-radius, var(--wa-border-radius-pill));
+  }
+
+  /*
+   * Label
+   */
+
+  .start,
+  .end {
+    flex: 0 0 auto;
+    display: flex;
+    align-items: center;
+    pointer-events: none;
+  }
+
+  .label {
+    display: inline-block;
+  }
+
+  .is-icon-button .label {
+    display: flex;
+    justify-content: center;
+  }
+
+  .label::slotted(wa-icon) {
+    align-self: center;
+  }
+
+  /*
+   * Caret modifier
+   */
+
+  wa-icon[part='caret'] {
+    display: flex;
+    align-self: center;
+    align-items: center;
+
+    &::part(svg) {
+      width: 0.875em;
+      height: 0.875em;
+    }
+
+    .button:has(&) .end {
+      display: none;
+    }
+  }
+
+  /*
+   * Loading modifier
+   */
+
+  .loading {
+    position: relative;
+    cursor: wait;
+
+    .start,
+    .label,
+    .end,
+    .caret {
+      visibility: hidden;
+    }
+
+    wa-spinner {
+      --indicator-color: currentColor;
+      --track-color: color-mix(in oklab, currentColor, transparent 90%);
+
+      position: absolute;
+      font-size: 1em;
+      height: 1em;
+      width: 1em;
+      top: calc(50% - 0.5em);
+      left: calc(50% - 0.5em);
+    }
+  }
+
+  /*
+   * Badges
+   */
+
+  .button ::slotted(wa-badge) {
+    border-color: var(--wa-color-surface-default);
+    position: absolute;
+    inset-block-start: 0;
+    inset-inline-end: 0;
+    translate: 50% -50%;
+    pointer-events: none;
+  }
+
+  :host(:dir(rtl)) ::slotted(wa-badge) {
+    translate: -50% -50%;
+  }
+
+  /*
+  * Button spacing
+  */
+
+  slot[name='start']::slotted(*) {
+    margin-inline-end: 0.75em;
+  }
+
+  slot[name='end']::slotted(*),
+  .button:not(.visually-hidden-label) [part='caret'] {
+    margin-inline-start: 0.75em;
+  }
+`;var di={small:"s",medium:"m",large:"l"},hi=new Set;function G(t,e){e in di&&!hi.has(`${t}:${e}`)&&(hi.add(`${t}:${e}`),console.warn(`[${t}] size="${e}" is deprecated. Use size="${di[e]}" instead. The long-form value will be removed in the next major version.`))}var et=w`
+  :host([size='xs']) {
+    font-size: var(--wa-font-size-xs);
+  }
+
+  :host([size='s']),
+  :host([size='small']) {
+    font-size: var(--wa-font-size-s);
+  }
+
+  :host([size='m']),
+  :host([size='medium']) {
+    font-size: var(--wa-font-size-m);
+  }
+
+  :host([size='l']),
+  :host([size='large']) {
+    font-size: var(--wa-font-size-l);
+  }
+
+  :host([size='xl']) {
+    font-size: var(--wa-font-size-xl);
+  }
+`;var Be=w`
+  :where(:root),
+  .wa-neutral,
+  :host([variant='neutral']) {
+    --wa-color-fill-loud: var(--wa-color-neutral-fill-loud);
+    --wa-color-fill-normal: var(--wa-color-neutral-fill-normal);
+    --wa-color-fill-quiet: var(--wa-color-neutral-fill-quiet);
+    --wa-color-border-loud: var(--wa-color-neutral-border-loud);
+    --wa-color-border-normal: var(--wa-color-neutral-border-normal);
+    --wa-color-border-quiet: var(--wa-color-neutral-border-quiet);
+    --wa-color-on-loud: var(--wa-color-neutral-on-loud);
+    --wa-color-on-normal: var(--wa-color-neutral-on-normal);
+    --wa-color-on-quiet: var(--wa-color-neutral-on-quiet);
+  }
+
+  .wa-brand,
+  :host([variant='brand']) {
+    --wa-color-fill-loud: var(--wa-color-brand-fill-loud);
+    --wa-color-fill-normal: var(--wa-color-brand-fill-normal);
+    --wa-color-fill-quiet: var(--wa-color-brand-fill-quiet);
+    --wa-color-border-loud: var(--wa-color-brand-border-loud);
+    --wa-color-border-normal: var(--wa-color-brand-border-normal);
+    --wa-color-border-quiet: var(--wa-color-brand-border-quiet);
+    --wa-color-on-loud: var(--wa-color-brand-on-loud);
+    --wa-color-on-normal: var(--wa-color-brand-on-normal);
+    --wa-color-on-quiet: var(--wa-color-brand-on-quiet);
+  }
+
+  .wa-success,
+  :host([variant='success']) {
+    --wa-color-fill-loud: var(--wa-color-success-fill-loud);
+    --wa-color-fill-normal: var(--wa-color-success-fill-normal);
+    --wa-color-fill-quiet: var(--wa-color-success-fill-quiet);
+    --wa-color-border-loud: var(--wa-color-success-border-loud);
+    --wa-color-border-normal: var(--wa-color-success-border-normal);
+    --wa-color-border-quiet: var(--wa-color-success-border-quiet);
+    --wa-color-on-loud: var(--wa-color-success-on-loud);
+    --wa-color-on-normal: var(--wa-color-success-on-normal);
+    --wa-color-on-quiet: var(--wa-color-success-on-quiet);
+  }
+
+  .wa-warning,
+  :host([variant='warning']) {
+    --wa-color-fill-loud: var(--wa-color-warning-fill-loud);
+    --wa-color-fill-normal: var(--wa-color-warning-fill-normal);
+    --wa-color-fill-quiet: var(--wa-color-warning-fill-quiet);
+    --wa-color-border-loud: var(--wa-color-warning-border-loud);
+    --wa-color-border-normal: var(--wa-color-warning-border-normal);
+    --wa-color-border-quiet: var(--wa-color-warning-border-quiet);
+    --wa-color-on-loud: var(--wa-color-warning-on-loud);
+    --wa-color-on-normal: var(--wa-color-warning-on-normal);
+    --wa-color-on-quiet: var(--wa-color-warning-on-quiet);
+  }
+
+  .wa-danger,
+  :host([variant='danger']) {
+    --wa-color-fill-loud: var(--wa-color-danger-fill-loud);
+    --wa-color-fill-normal: var(--wa-color-danger-fill-normal);
+    --wa-color-fill-quiet: var(--wa-color-danger-fill-quiet);
+    --wa-color-border-loud: var(--wa-color-danger-border-loud);
+    --wa-color-border-normal: var(--wa-color-danger-border-normal);
+    --wa-color-border-quiet: var(--wa-color-danger-border-quiet);
+    --wa-color-on-loud: var(--wa-color-danger-on-loud);
+    --wa-color-on-normal: var(--wa-color-danger-on-normal);
+    --wa-color-on-quiet: var(--wa-color-danger-on-quiet);
+  }
+`;function S(t,e){let o={waitUntilFirstUpdate:!1,...e};return(i,r)=>{let{update:a}=i,n=Array.isArray(t)?t:[t];i.update=function(c){n.forEach(u=>{let d=u;if(c.has(d)){let h=c.get(d),p=this[d];h!==p&&(!o.waitUntilFirstUpdate||this.hasUpdated)&&this[r](h,p)}}),a.call(this,c)}}}var go=new Set,Yt=new Map,yt,vo="ltr",wo="en",pi=typeof MutationObserver<"u"&&typeof document<"u"&&typeof document.documentElement<"u";if(pi){let t=new MutationObserver(mi);vo=document.documentElement.dir||"ltr",wo=do [...]
+            take care to ensure page security.`)})(i)+t[r+1],t[0]),r:vi}),gi=new Map,Co=t=>(e,...o)=>{let i=o.length,r,a,n=[],c=[],u,d=0,h=!1;for(;d<i;){for(u=e[d];d<i&&(a=o[d],(r=ea(a))!==void 0);)u+=r+e[++d],h=!0;d!==i&&c.push(a),n.push(u),d++}if(d===i&&n.push(e[i]),h){let p=n.join("$$lit$$");(e=gi.get(p))===void 0&&(n.raw=n,gi.set(p,e=n)),o=c}return t(e,...o)},Ve=Co(g),zs=Co(ii),$s=Co(ri);var L=class extends V{constructor(){super(...arguments),this.assumeInteractionOn=["click"],this.h [...]
+      <${e}
+        part="base button"
+        class=${M({button:!0,caret:this.withCaret,disabled:this.disabled,loading:this.loading,rtl:this.localize.dir()==="rtl","has-label":this.hasSlotController.test("[default]"),"has-start":this.hasSlotController.test("start","withStart"),"has-end":this.hasSlotController.test("end","withEnd"),"is-icon-button":this.isIconButton})}
+        ?disabled=${k(t?void 0:this.disabled)}
+        type=${k(t?void 0:this.type)}
+        title=${this.title}
+        name=${k(t?void 0:this.name)}
+        value=${k(t?void 0:this.value)}
+        href=${k(t?this.href:void 0)}
+        target=${k(t?this.target:void 0)}
+        download=${k(t?this.download:void 0)}
+        rel=${k(t&&this.rel?this.rel:void 0)}
+        role=${k(t?void 0:"button")}
+        aria-disabled=${k(t&&this.disabled?"true":void 0)}
+        tabindex=${this.disabled?"-1":"0"}
+        @invalid=${this.isButton()?this.handleInvalid:null}
+        @click=${this.handleClick}
+      >
+        <slot name="start" part="start" class="start"></slot>
+        <slot part="label" class="label" @slotchange=${this.handleLabelSlotChange}></slot>
+        <slot name="end" part="end" class="end"></slot>
+        ${this.withCaret?Ve`
+                <wa-icon part="caret" class="caret" library="system" name="chevron-down" variant="solid"></wa-icon>
+              `:""}
+        ${this.loading?Ve`<wa-spinner part="spinner"></wa-spinner>`:""}
+      </${e}>
+    `}};L.shadowRootOptions={...V.shadowRootOptions,delegatesFocus:!0};L.css=[ui,Be,et];s([z(".button")],L.prototype,"button",2);s([z("slot:not([name])")],L.prototype,"labelSlot",2);s([B()],L.prototype,"invalid",2);s([B()],L.prototype,"isIconButton",2);s([l()],L.prototype,"title",2);s([l({reflect:!0})],L.prototype,"variant",2);s([l({reflect:!0})],L.prototype,"appearance",2);s([l({reflect:!0})],L.prototype,"size",2);s([S("size")],L.prototype,"handleSizeChange",1);s([l({attribute:"with-car [...]
+  :host {
+    --track-width: 2px;
+    --track-color: var(--wa-color-neutral-fill-normal);
+    --indicator-color: var(--wa-color-brand-fill-loud);
+    --speed: 2s;
+    --size: 1em;
+
+    /*
+      Resizing a spinner element using anything but font-size will break the animation because the animation uses em
+      units. Therefore, if a spinner is used in a flex container without \`flex: none\` applied, the spinner can
+      grow/shrink and break the animation. The use of \`flex: none\` on the host element prevents this by always having
+      the spinner sized according to its actual dimensions.
+    */
+    flex: none;
+    display: inline-flex;
+    width: var(--size);
+    height: var(--size);
+  }
+
+  svg {
+    width: 100%;
+    height: 100%;
+    aspect-ratio: 1;
+    animation: spin var(--speed) linear infinite;
+  }
+
+  .track,
+  .indicator {
+    --radius: calc(var(--size) / 2 - var(--track-width) / 2);
+    --circumference: calc(var(--radius) * 2 * 3.141592654);
+
+    cx: calc(var(--size) / 2);
+    cy: calc(var(--size) / 2);
+    r: var(--radius);
+    fill: none;
+    stroke-width: var(--track-width);
+  }
+
+  .track {
+    stroke: var(--track-color);
+  }
+
+  .indicator {
+    stroke: var(--indicator-color);
+    stroke-linecap: round;
+    stroke-dasharray: calc(0.597 * var(--circumference)), calc(0.796 * var(--circumference));
+    stroke-dashoffset: calc(-0.04 * var(--circumference));
+    animation: dash 1.5s ease-in-out infinite;
+  }
+
+  @keyframes spin {
+    0% {
+      transform: rotate(0deg);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+
+  @keyframes dash {
+    0% {
+      stroke-dasharray: calc(0.008 * var(--circumference)), calc(1.194 * var(--circumference));
+      stroke-dashoffset: 0;
+    }
+    50% {
+      stroke-dasharray: calc(0.716 * var(--circumference)), calc(1.194 * var(--circumference));
+      stroke-dashoffset: calc(-0.278 * var(--circumference));
+    }
+    100% {
+      stroke-dasharray: calc(0.716 * var(--circumference)), calc(1.194 * var(--circumference));
+      stroke-dashoffset: calc(-0.987 * var(--circumference));
+    }
+  }
+`;var Ne=class extends P{constructor(){super(...arguments),this.localize=new I(this)}render(){return g`
+      <svg
+        part="base spinner"
+        role="progressbar"
+        aria-label=${this.localize.term("loading")}
+        fill="none"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <circle class="track" />
+        <circle class="indicator" />
+      </svg>
+    `}};Ne.css=wi;Ne=s([$("wa-spinner")],Ne);var yi=class extends Event{constructor(){super("wa-error",{bubbles:!0,cancelable:!1,composed:!0})}};var Ci=class extends Event{constructor(){super("wa-load",{bubbles:!0,cancelable:!1,composed:!0})}};var xi=w`
+  :host {
+    --primary-color: currentColor;
+    --primary-opacity: 1;
+    --secondary-color: currentColor;
+    --secondary-opacity: 0.4;
+    --rotate-angle: 0deg;
+
+    box-sizing: content-box;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    vertical-align: -0.125em;
+  }
+
+  /* #region Canvas — the box the icon is centered within (mirrors Font Awesome's icon canvas). Orthogonal to font-size. */
+
+  /* Fixed width (default): 1.25em × 1em (20 × 16px) */
+  :host(:not([canvas])),
+  :host([canvas='fixed']) {
+    width: 1.25em;
+    height: 1em;
+    min-width: 1.25em; /* <-- this is what Safari respects for intrinsic */
+    min-height: 1em;
+  }
+
+  /* Auto: hug the icon's width. \`auto-width\` is the deprecated alias for canvas="auto". */
+  :host([canvas='auto']),
+  :host([auto-width]:not([canvas])) {
+    width: auto;
+    height: 1em;
+  }
+
+  /* Square: 1.25em × 1.25em (20 × 20px) */
+  :host([canvas='square']) {
+    width: 1.25em;
+    height: 1.25em;
+    min-width: 1.25em;
+    min-height: 1.25em;
+  }
+
+  /* Roomy: 1.5em × 1.5em (24 × 24px) */
+  :host([canvas='roomy']) {
+    width: 1.5em;
+    height: 1.5em;
+    min-width: 1.5em;
+    min-height: 1.5em;
+  }
+
+  /* #endregion */
+
+  svg {
+    /* NOTE: Avoid setting fill here. A stylesheet rule beats SVG presentation attributes, breaking stroke-based
+       libraries like Lucide (fill="none" stroke="currentColor") and attribute-based mutators (issue #1733). The default
+       library applies fill="currentColor" in its mutator instead. */
+    height: 1em;
+    overflow: visible;
+    width: auto;
+
+    /* Duotone colors with path-specific opacity fallback */
+    path[data-duotone-primary] {
+      color: var(--primary-color);
+      opacity: var(--path-opacity, var(--primary-opacity));
+    }
+
+    path[data-duotone-secondary] {
+      color: var(--secondary-color);
+      opacity: var(--path-opacity, var(--secondary-opacity));
+    }
+  }
+
+  /* Rotation */
+  :host([rotate]) {
+    transform: rotate(var(--rotate-angle, 0deg));
+  }
+
+  /* Flipping */
+  :host([flip='x']) {
+    transform: scaleX(-1);
+  }
+  :host([flip='y']) {
+    transform: scaleY(-1);
+  }
+  :host([flip='both']) {
+    transform: scale(-1, -1);
+  }
+
+  /* Rotation and Flipping combined */
+  :host([rotate][flip='x']) {
+    transform: rotate(var(--rotate-angle, 0deg)) scaleX(-1);
+  }
+  :host([rotate][flip='y']) {
+    transform: rotate(var(--rotate-angle, 0deg)) scaleY(-1);
+  }
+  :host([rotate][flip='both']) {
+    transform: rotate(var(--rotate-angle, 0deg)) scale(-1, -1);
+  }
+
+  /* #region Animations — ported from Font Awesome 7.3 (--fa-* props mapped to wa-icon's --* names) */
+
+  :host([animation='beat']) {
+    animation-name: beat;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+  }
+
+  :host([animation='bounce']) {
+    animation-name: bounce;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1));
+  }
+
+  :host([animation='fade']) {
+    animation-name: fade;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+  }
+
+  :host([animation='beat-fade']) {
+    animation-name: beat-fade;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+  }
+
+  :host([animation='flip']) {
+    animation-name: flip;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1.5s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+  }
+
+  :host([animation='flip-360']) {
+    animation-name: flip-360;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+  }
+
+  :host([animation='shake']) {
+    animation-name: shake;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 0.75s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+  }
+
+  :host([animation='spin']) {
+    animation-name: spin;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 2s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, linear);
+  }
+
+  :host([animation='spin-pulse']) {
+    animation-name: spin;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, steps(8));
+  }
+
+  /* spin-reverse is FA's reverse modifier expressed as a standalone value; reverse any spin via --animation-direction: reverse */
+  :host([animation='spin-reverse']) {
+    animation-name: spin;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, reverse);
+    animation-duration: var(--animation-duration, 2s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, linear);
+  }
+
+  :host([animation='spin-snap']) {
+    animation-name: spin-snap;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 3s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, linear);
+  }
+
+  :host([animation='spin-snap-4']) {
+    animation-name: spin-snap-4;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 2.4s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, linear);
+  }
+
+  :host([animation='spin-snap-8']) {
+    animation-name: spin-snap-8;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 4s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, linear);
+  }
+
+  :host([animation='buzz']) {
+    animation-name: buzz;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 0.6s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, linear);
+  }
+
+  :host([animation='wag']) {
+    animation-name: wag;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 0.9s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-out);
+    transform-origin: bottom center;
+  }
+
+  :host([animation='float']) {
+    animation-name: float;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 3s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-in-out);
+    will-change: transform;
+  }
+
+  :host([animation='swing']) {
+    animation-name: swing;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 1.2s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-out);
+    transform-origin: top center;
+  }
+
+  :host([animation='jello']) {
+    animation-name: jello;
+    animation-delay: var(--animation-delay, 0s);
+    animation-direction: var(--animation-direction, normal);
+    animation-duration: var(--animation-duration, 0.9s);
+    animation-iteration-count: var(--animation-iteration-count, infinite);
+    animation-timing-function: var(--animation-timing, ease-out);
+  }
+
+  @media (prefers-reduced-motion: reduce) {
+    :host([animation='beat']),
+    :host([animation='bounce']),
+    :host([animation='fade']),
+    :host([animation='beat-fade']),
+    :host([animation='flip']),
+    :host([animation='flip-360']),
+    :host([animation='shake']),
+    :host([animation='spin']),
+    :host([animation='spin-pulse']),
+    :host([animation='spin-reverse']),
+    :host([animation='spin-snap']),
+    :host([animation='spin-snap-4']),
+    :host([animation='spin-snap-8']),
+    :host([animation='buzz']),
+    :host([animation='wag']),
+    :host([animation='float']),
+    :host([animation='swing']),
+    :host([animation='jello']) {
+      animation: none !important;
+      transition: none !important;
+    }
+  }
+
+  /* #endregion */
+
+  /* #region Keyframes — ported verbatim from Font Awesome 7.3 */
+
+  @keyframes beat {
+    0% {
+      transform: scale(1);
+    }
+    25% {
+      transform: scale(calc(1.25 * var(--beat-scale, 1.25)));
+    }
+    45% {
+      transform: scale(calc(1.22 * var(--beat-scale, 1.22)));
+    }
+    65% {
+      transform: scale(calc(1.25 * var(--beat-scale, 1.25)));
+    }
+    90% {
+      transform: scale(1);
+    }
+  }
+
+  @keyframes bounce {
+    0% {
+      transform: scale(1, 1) translateY(0);
+      /* No fallback by design (ported from FA 7.3): the first segment uses the user's --animation-timing or the CSS
+         initial ease, while the explicit cubic-beziers on later stops drive the bounce physics. */
+      animation-timing-function: var(--animation-timing);
+    }
+    14% {
+      transform: scale(var(--bounce-start-scale-x, 1.06), var(--bounce-start-scale-y, 0.94))
+        translateY(var(--bounce-anticipation, 3px));
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+    }
+    32% {
+      transform: scale(var(--bounce-jump-scale-x, 0.94), var(--bounce-jump-scale-y, 1.12))
+        translateY(calc(-1 * var(--bounce-height, 0.5em)));
+      animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+    }
+    52% {
+      transform: scale(1, 1) translateY(calc(-1 * var(--bounce-height, 0.5em) * 1.1));
+      animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
+    }
+    70% {
+      transform: scale(var(--bounce-land-scale-x, 1.06), var(--bounce-land-scale-y, 0.92)) translateY(0);
+      animation-timing-function: cubic-bezier(0.33, 0.33, 0.66, 1);
+    }
+    85% {
+      transform: scale(0.98, 1.04) translateY(calc(-2px * var(--bounce-rebound, 1)));
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 1);
+    }
+    100% {
+      transform: scale(1, 1) translateY(0);
+    }
+  }
+
+  @keyframes fade {
+    0% {
+      opacity: 1;
+      transform: scale(1);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.4, 1);
+    }
+    40% {
+      opacity: var(--fade-opacity, 0.4);
+      transform: scale(0.98);
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    100% {
+      opacity: 1;
+      transform: scale(1);
+    }
+  }
+
+  @keyframes beat-fade {
+    0% {
+      opacity: var(--beat-fade-opacity, 0.4);
+      transform: scale(1);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.4, 1);
+    }
+    25% {
+      opacity: calc(var(--beat-fade-opacity, 0.4) + 0.4);
+      transform: scale(var(--beat-fade-scale, 1.28));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    45% {
+      opacity: 1;
+      transform: scale(var(--beat-fade-scale, 1.25));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+    }
+    65% {
+      opacity: calc(var(--beat-fade-opacity, 0.4) + 0.4);
+      transform: scale(var(--beat-fade-scale, 1.28));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    100% {
+      opacity: var(--beat-fade-opacity, 0.4);
+      transform: scale(1);
+    }
+  }
+
+  @keyframes flip {
+    0% {
+      transform: perspective(2em) scale(1) rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), 0deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.4, 1);
+    }
+    8% {
+      transform: perspective(2em) scale(var(--flip-anticipation-scale, 0.95))
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), 0deg);
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+    }
+    35% {
+      transform: perspective(2em) scale(1)
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), calc(var(--flip-angle, -360deg) * 0.6));
+      animation-timing-function: linear;
+    }
+    65% {
+      transform: perspective(2em) scale(1)
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), calc(var(--flip-angle, -360deg) * 0.5));
+      animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+    }
+    92% {
+      transform: perspective(2em) scale(1)
+        rotate3d(
+          var(--flip-x, 0),
+          var(--flip-y, 1),
+          var(--flip-z, 0),
+          calc(var(--flip-angle, -360deg) * var(--flip-overshoot, 1.04))
+        );
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 1);
+    }
+    100% {
+      transform: perspective(2em) scale(1)
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), var(--flip-angle, -360deg));
+    }
+  }
+
+  @keyframes flip-360 {
+    0% {
+      transform: perspective(2em) scale(1) rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), 0deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.4, 1);
+    }
+    8% {
+      transform: perspective(2em) scale(var(--flip-anticipation-scale, 0.95))
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), 0deg);
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+    }
+    50% {
+      transform: perspective(2em) scale(1)
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), calc(var(--flip-angle, -360deg) * 0.6));
+      animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+    }
+    80% {
+      transform: perspective(2em) scale(1)
+        rotate3d(
+          var(--flip-x, 0),
+          var(--flip-y, 1),
+          var(--flip-z, 0),
+          calc(var(--flip-angle, -360deg) * var(--flip-overshoot, 1.04))
+        );
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 1);
+    }
+    100% {
+      transform: perspective(2em) scale(1)
+        rotate3d(var(--flip-x, 0), var(--flip-y, 1), var(--flip-z, 0), var(--flip-angle, -360deg));
+    }
+  }
+
+  @keyframes shake {
+    0% {
+      transform: rotate(0deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.8, 1);
+    }
+    8% {
+      transform: rotate(35deg) translateX(1px);
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    20% {
+      transform: rotate(-22deg) translateX(-1px);
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    35% {
+      transform: rotate(15deg) translateX(1px);
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    50% {
+      transform: rotate(-9deg);
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    65% {
+      transform: rotate(5deg);
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    78% {
+      transform: rotate(-3deg);
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    90% {
+      transform: rotate(1deg);
+      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+    }
+    100% {
+      transform: rotate(0deg);
+    }
+  }
+
+  @keyframes spin {
+    0% {
+      transform: rotate(0deg);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+
+  @keyframes spin-snap {
+    0% {
+      transform: rotate(0deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    12% {
+      transform: rotate(60deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    16.67% {
+      transform: rotate(60deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    28.67% {
+      transform: rotate(120deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    33.33% {
+      transform: rotate(120deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    45.33% {
+      transform: rotate(180deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    50% {
+      transform: rotate(180deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    62% {
+      transform: rotate(240deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    66.67% {
+      transform: rotate(240deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    78.67% {
+      transform: rotate(300deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    83.33% {
+      transform: rotate(300deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    95.33% {
+      transform: rotate(360deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+
+  @keyframes spin-snap-4 {
+    0% {
+      transform: rotate(0deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    15% {
+      transform: rotate(90deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    25% {
+      transform: rotate(90deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    40% {
+      transform: rotate(180deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    50% {
+      transform: rotate(180deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    65% {
+      transform: rotate(270deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    75% {
+      transform: rotate(270deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    90% {
+      transform: rotate(360deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+
+  @keyframes spin-snap-8 {
+    0% {
+      transform: rotate(0deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    9% {
+      transform: rotate(45deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    12.5% {
+      transform: rotate(45deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    21.5% {
+      transform: rotate(90deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    25% {
+      transform: rotate(90deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    34% {
+      transform: rotate(135deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    37.5% {
+      transform: rotate(135deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    46.5% {
+      transform: rotate(180deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    50% {
+      transform: rotate(180deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    59% {
+      transform: rotate(225deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    62.5% {
+      transform: rotate(225deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    71.5% {
+      transform: rotate(270deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    75% {
+      transform: rotate(270deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    84% {
+      transform: rotate(315deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    87.5% {
+      transform: rotate(315deg);
+      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
+    }
+    96.5% {
+      transform: rotate(360deg);
+      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+
+  @keyframes buzz {
+    0% {
+      transform: translateX(0) rotate(0deg);
+      animation-timing-function: cubic-bezier(0.1, 0, 0.9, 1);
+    }
+    5% {
+      transform: translateX(var(--buzz-distance, 4px)) rotate(0.5deg);
+    }
+    10% {
+      transform: translateX(calc(-1 * var(--buzz-distance, 4px))) rotate(-0.5deg);
+    }
+    15% {
+      transform: translateX(var(--buzz-distance, 4px)) rotate(0.3deg);
+    }
+    20% {
+      transform: translateX(calc(-1 * var(--buzz-distance, 4px))) rotate(-0.3deg);
+    }
+    25% {
+      transform: translateX(calc(var(--buzz-distance, 4px) * 0.7)) rotate(0.2deg);
+    }
+    30% {
+      transform: translateX(calc(-1 * var(--buzz-distance, 4px) * 0.7)) rotate(-0.2deg);
+    }
+    35% {
+      transform: translateX(calc(var(--buzz-distance, 4px) * 0.4)) rotate(0.1deg);
+    }
+    40% {
+      transform: translateX(0) rotate(0deg);
+    }
+    100% {
+      transform: translateX(0) rotate(0deg);
+    }
+  }
+
+  @keyframes wag {
+    0% {
+      transform: rotate(0deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.6, 1);
+    }
+    12% {
+      transform: rotate(var(--wag-angle, 12deg));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+    }
+    24% {
+      transform: rotate(2deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.6, 1);
+    }
+    36% {
+      transform: rotate(calc(var(--wag-angle, 12deg) * 0.85));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+    }
+    48% {
+      transform: rotate(1deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.6, 1);
+    }
+    58% {
+      transform: rotate(calc(var(--wag-angle, 12deg) * 0.6));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+    }
+    68% {
+      transform: rotate(0deg);
+    }
+    100% {
+      transform: rotate(0deg);
+    }
+  }
+
+  @keyframes float {
+    0% {
+      transform: translateY(0) translateX(0) rotate(0deg)
+        scale(var(--float-squash-x, 1.02), var(--float-squash-y, 0.98));
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+    }
+    15% {
+      transform: translateY(calc(-0.4 * var(--float-height, 6px))) translateX(var(--float-drift, 1px))
+        rotate(var(--float-tilt, 1deg)) scale(1, 1);
+      animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+    }
+    35% {
+      transform: translateY(calc(-1 * var(--float-height, 6px))) translateX(0) rotate(0deg)
+        scale(var(--float-stretch-x, 0.98), var(--float-stretch-y, 1.03));
+      animation-timing-function: cubic-bezier(0.5, 0, 0.5, 0);
+    }
+    50% {
+      transform: translateY(calc(-0.92 * var(--float-height, 6px))) translateX(calc(-0.5 * var(--float-drift, 1px)))
+        rotate(calc(-0.5 * var(--float-tilt, 1deg))) scale(0.995, 1.01);
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+    }
+    70% {
+      transform: translateY(calc(-0.3 * var(--float-height, 6px))) translateX(calc(-1 * var(--float-drift, 1px)))
+        rotate(calc(-1 * var(--float-tilt, 1deg))) scale(1, 1);
+      animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+    }
+    90% {
+      transform: translateY(calc(0.05 * var(--float-height, 6px))) translateX(0) rotate(0deg)
+        scale(var(--float-squash-x, 1.02), var(--float-squash-y, 0.98));
+      animation-timing-function: cubic-bezier(0.33, 0, 0.66, 1);
+    }
+    100% {
+      transform: translateY(0) translateX(0) rotate(0deg)
+        scale(var(--float-squash-x, 1.02), var(--float-squash-y, 0.98));
+    }
+  }
+
+  @keyframes swing {
+    0% {
+      transform: rotate(0deg);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.8, 1);
+    }
+    8% {
+      transform: rotate(var(--swing-angle, 22deg));
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    18% {
+      transform: rotate(calc(-1 * var(--swing-angle, 22deg) * 0.85));
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    28% {
+      transform: rotate(calc(var(--swing-angle, 22deg) * 0.65));
+      animation-timing-function: cubic-bezier(0.35, 0, 0.65, 1);
+    }
+    38% {
+      transform: rotate(calc(-1 * var(--swing-angle, 22deg) * 0.45));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    48% {
+      transform: rotate(calc(var(--swing-angle, 22deg) * 0.25));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    56% {
+      transform: rotate(calc(-1 * var(--swing-angle, 22deg) * 0.1));
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    64% {
+      transform: rotate(0deg);
+    }
+    100% {
+      transform: rotate(0deg);
+    }
+  }
+
+  @keyframes jello {
+    0% {
+      transform: scale(1, 1);
+      animation-timing-function: cubic-bezier(0.2, 0, 0.8, 1);
+    }
+    12% {
+      transform: scale(var(--jello-scale-x, 1.15), calc(2 - var(--jello-scale-x, 1.15)));
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    24% {
+      transform: scale(calc(2 - var(--jello-scale-y, 1.12)), var(--jello-scale-y, 1.12));
+      animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1);
+    }
+    36% {
+      transform: scale(
+        calc(1 + (var(--jello-scale-x, 1.15) - 1) * 0.5),
+        calc(2 - (1 + (var(--jello-scale-x, 1.15) - 1) * 0.5))
+      );
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    48% {
+      transform: scale(
+        calc(2 - (1 + (var(--jello-scale-y, 1.12) - 1) * 0.3)),
+        calc(1 + (var(--jello-scale-y, 1.12) - 1) * 0.3)
+      );
+      animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
+    }
+    58% {
+      transform: scale(1.02, 0.98);
+      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+    }
+    68% {
+      transform: scale(1, 1);
+    }
+    100% {
+      transform: scale(1, 1);
+    }
+  }
+
+  /* #endregion */
+`;var oa="",xo="";function Si(){return oa.replace(/\/$/,"")}function ia(t){xo=t}function Li(){if(!xo){let t=document.querySelector("[data-fa-kit-code]");t&&ia(t.getAttribute("data-fa-kit-code")||"")}return xo}var Ai="7.3.0";function ra(t,e,o){let i="solid";return e==="chisel"&&(i="chisel-regular"),e==="etch"&&(i="etch-solid"),e==="graphite"&&(i="graphite-thin"),e==="jelly"&&(i="jelly-regular",o==="duo-regular"&&(i="jelly-duo-regular"),o==="fill-regular"&&(i="jelly-fill-regular")),e==="je [...]
+        <use part="use" href="${t}"></use>
+      </svg>`,await this.updateComplete;let i=this.shadowRoot.querySelector("[part='svg']");return typeof e.mutator=="function"&&e.mutator(i,this),this.svg}try{if(o=await fetch(t,{mode:"cors"}),!o.ok)return o.status===410?ge:We}catch{return We}try{let i=document.createElement("div");i.innerHTML=await o.text();let r=i.firstElementChild;if(r?.tagName?.toLowerCase()!=="svg")return ge;Lo||(Lo=new DOMParser);let n=Lo.parseFromString(r.outerHTML,"text/html").body.querySelector("svg");return n? [...]
+  :host {
+    --width: 31rem;
+    --spacing: var(--wa-space-l);
+    --backdrop-filter: none;
+    --show-duration: var(--wa-transition-normal);
+    --hide-duration: var(--wa-transition-normal);
+
+    display: none;
+  }
+
+  :host([open]) {
+    display: block;
+  }
+
+  .dialog {
+    display: flex;
+    flex-direction: column;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    width: var(--width);
+    max-width: calc(100% - var(--wa-space-2xl));
+    max-height: calc(100% - var(--wa-space-2xl));
+    color: inherit;
+    background-color: var(--wa-color-surface-raised);
+    border-radius: var(--wa-panel-border-radius);
+    border: none;
+    box-shadow: var(--wa-shadow-l);
+    padding: 0;
+    margin: auto;
+
+    &.show {
+      animation: show-dialog var(--show-duration) ease;
+
+      &::backdrop {
+        animation: show-backdrop var(--show-duration, 200ms) ease;
+      }
+    }
+
+    &.hide {
+      animation: show-dialog var(--hide-duration) ease reverse;
+
+      &::backdrop {
+        animation: show-backdrop var(--hide-duration, 200ms) ease reverse;
+      }
+    }
+
+    &.pulse {
+      animation: pulse 250ms ease;
+    }
+  }
+
+  .dialog:focus {
+    outline: none;
+  }
+
+  /* Ensure there's enough vertical padding for phones that don't update vh when chrome appears (e.g. iPhone) */
+  @media screen and (max-width: 420px) {
+    .dialog {
+      max-height: 80vh;
+    }
+  }
+
+  .open {
+    display: flex;
+    opacity: 1;
+  }
+
+  .header {
+    flex: 0 0 auto;
+    display: flex;
+    flex-wrap: nowrap;
+
+    padding-inline-start: var(--spacing);
+    padding-block-end: 0;
+
+    /* Subtract the close button's padding so that the X is visually aligned with the edges of the dialog content */
+    padding-inline-end: calc(var(--spacing) - var(--wa-form-control-padding-block));
+    padding-block-start: calc(var(--spacing) - var(--wa-form-control-padding-block));
+  }
+
+  .title {
+    align-self: center;
+    flex: 1 1 auto;
+    font-family: inherit;
+    font-size: var(--wa-font-size-l);
+    font-weight: var(--wa-font-weight-heading);
+    line-height: var(--wa-line-height-condensed);
+    margin: 0;
+  }
+
+  .header-actions {
+    align-self: start;
+    display: flex;
+    flex-shrink: 0;
+    flex-wrap: wrap;
+    justify-content: end;
+    gap: var(--wa-space-2xs);
+    padding-inline-start: var(--spacing);
+  }
+
+  .header-actions wa-button,
+  .header-actions ::slotted(wa-button) {
+    flex: 0 0 auto;
+    display: flex;
+    align-items: center;
+  }
+
+  .body {
+    flex: 1 1 auto;
+    display: block;
+    padding: var(--spacing);
+    overflow: auto;
+    -webkit-overflow-scrolling: touch;
+
+    &:focus {
+      outline: none;
+    }
+
+    &:focus-visible {
+      outline: var(--wa-focus-ring);
+      outline-offset: var(--wa-focus-ring-offset);
+    }
+  }
+
+  .footer {
+    flex: 0 0 auto;
+    display: flex;
+    flex-wrap: wrap;
+    gap: var(--wa-space-xs);
+    justify-content: end;
+    padding: var(--spacing);
+    padding-block-start: 0;
+  }
+
+  .footer ::slotted(wa-button:not(:first-of-type)) {
+    margin-inline-start: var(--wa-spacing-xs);
+  }
+
+  .dialog::backdrop {
+    /*
+      NOTE: the ::backdrop element doesn't inherit properly in Safari yet, but it will in 17.4! At that time, we can
+      remove the fallback values here.
+    */
+    background-color: var(--wa-color-overlay-modal, rgb(0 0 0 / 0.25));
+    backdrop-filter: var(--backdrop-filter);
+  }
+
+  @keyframes pulse {
+    0% {
+      scale: 1;
+    }
+    50% {
+      scale: 1.02;
+    }
+    100% {
+      scale: 1;
+    }
+  }
+
+  @keyframes show-dialog {
+    from {
+      opacity: 0;
+      scale: 0.8;
+    }
+    to {
+      opacity: 1;
+      scale: 1;
+    }
+  }
+
+  @keyframes show-backdrop {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+
+  @media (forced-colors: active) {
+    .dialog {
+      border: solid 1px white;
+    }
+  }
+`;var Ft=[];function oe(t){Ft.push(t)}function Bt(t){for(let e=Ft.length-1;e>=0;e--)if(Ft[e]===t){Ft.splice(e,1);break}}function qt(t){return Ft.length>0&&Ft[Ft.length-1]===t}function Q(t,e){return new Promise(o=>{let i=new AbortController,{signal:r}=i;if(t.classList.contains(e))return;t.classList.add(e);let a=!1,n=()=>{a||(a=!0,t.classList.remove(e),o(),i.abort())};t.addEventListener("animationend",n,{once:!0,signal:r}),t.addEventListener("animationcancel",n,{once:!0,signal:r}),requestA [...]
+      <dialog
+        part="dialog"
+        class=${M({dialog:!0,open:this.open})}
+        @cancel=${this.handleDialogCancel}
+        @click=${this.handleDialogClick}
+        @pointerdown=${this.handleDialogPointerDown}
+      >
+        ${t?g`
+              <header part="header" class="header">
+                <h2 part="title" class="title" id="title">
+                  <!-- If there's no label, use an invisible character to prevent the header from collapsing -->
+                  <slot name="label"> ${this.label.length>0?this.label:"\u200B"} </slot>
+                </h2>
+                <div part="header-actions" class="header-actions">
+                  <slot name="header-actions"></slot>
+                  <wa-button
+                    part="close-button"
+                    exportparts="base:close-button__base"
+                    class="close"
+                    appearance="plain"
+                    @click="${o=>this.requestClose(o.target)}"
+                  >
+                    <wa-icon
+                      name="xmark"
+                      label=${this.localize.term("close")}
+                      library="system"
+                      variant="solid"
+                    ></wa-icon>
+                  </wa-button>
+                </div>
+              </header>
+            `:""}
+
+        <div part="body" class="body"><slot></slot></div>
+
+        <!-- Use a hidden element so we still get "slotchange" events. -->
+        <footer part="footer" class="footer" ?hidden=${!e}>
+          <slot name="footer"></slot>
+        </footer>
+      </dialog>
+    `}};nt.css=Ii;s([z(".dialog")],nt.prototype,"dialog",2);s([l({type:Boolean,reflect:!0})],nt.prototype,"open",2);s([l({reflect:!0})],nt.prototype,"label",2);s([l({attribute:"without-header",type:Boolean,reflect:!0})],nt.prototype,"withoutHeader",2);s([l({attribute:"light-dismiss",type:Boolean})],nt.prototype,"lightDismiss",2);s([l({attribute:"with-footer",type:Boolean})],nt.prototype,"withFooter",2);s([S("open",{waitUntilFirstUpdate:!0})],nt.prototype,"handleOpenChange",1);nt=s([$("wa [...]
+  :host {
+    --show-duration: var(--wa-transition-fast);
+    --hide-duration: var(--wa-transition-fast);
+    display: contents;
+  }
+
+  #menu {
+    display: flex;
+    flex-direction: column;
+    width: max-content;
+    margin: 0;
+    padding: 0.25em;
+    border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
+    border-radius: var(--wa-border-radius-m);
+    background-color: var(--wa-color-surface-raised);
+    box-shadow: var(--wa-shadow-m);
+    color: var(--wa-color-text-normal);
+    text-align: start;
+    user-select: none;
+    overflow: auto;
+    max-width: var(--auto-size-available-width) !important;
+    max-height: var(--auto-size-available-height) !important;
+
+    &.show {
+      animation: show var(--show-duration) ease;
+    }
+
+    &.hide {
+      animation: show var(--hide-duration) ease reverse;
+    }
+
+    ::slotted(h1),
+    ::slotted(h2),
+    ::slotted(h3),
+    ::slotted(h4),
+    ::slotted(h5),
+    ::slotted(h6) {
+      display: block !important;
+      margin: 0.25em 0 !important;
+      padding: 0.25em 0.75em !important;
+      color: var(--wa-color-text-quiet);
+      font-family: var(--wa-font-family-body) !important;
+      font-weight: var(--wa-font-weight-semibold) !important;
+      font-size: var(--wa-font-size-smaller) !important;
+    }
+
+    ::slotted(wa-divider) {
+      --spacing: 0.25em; /* Component-specific, left as-is */
+    }
+  }
+
+  wa-popup[data-current-placement^='top'] #menu {
+    transform-origin: bottom;
+  }
+
+  wa-popup[data-current-placement^='bottom'] #menu {
+    transform-origin: top;
+  }
+
+  wa-popup[data-current-placement^='left'] #menu {
+    transform-origin: right;
+  }
+
+  wa-popup[data-current-placement^='right'] #menu {
+    transform-origin: left;
+  }
+
+  wa-popup[data-current-placement='left-start'] #menu {
+    transform-origin: right top;
+  }
+
+  wa-popup[data-current-placement='left-end'] #menu {
+    transform-origin: right bottom;
+  }
+
+  wa-popup[data-current-placement='right-start'] #menu {
+    transform-origin: left top;
+  }
+
+  wa-popup[data-current-placement='right-end'] #menu {
+    transform-origin: left bottom;
+  }
+
+  @keyframes show {
+    from {
+      scale: 0.9;
+      opacity: 0;
+    }
+    to {
+      scale: 1;
+      opacity: 1;
+    }
+  }
+`;var qi="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var Vi=(t=21)=>{let e="",o=crypto.getRandomValues(new Uint8Array(t|=0));for(;t--;)e+=qi[o[t]&63];return e};function Ni(t=""){return`${t}${Vi()}`}var pt=Math.min,st=Math.max,we=Math.round,ye=Math.floor,mt=t=>({x:t,y:t}),fa={left:"right",right:"left",bottom:"top",top:"bottom"};function zo(t,e,o){return st(t,pt(e,o))}function Vt(t,e){return typeof t=="function"?t(e):t}function Et(t){return t.split("-")[0]}function N [...]
+      <wa-popup
+        placement=${this.placement}
+        distance=${this.distance}
+        skidding=${this.skidding}
+        ?active=${t}
+        flip
+        flip-fallback-strategy="best-fit"
+        shift
+        shift-padding="10"
+        auto-size="vertical"
+        auto-size-padding="10"
+      >
+        <slot
+          name="trigger"
+          slot="anchor"
+          @click=${this.handleTriggerClick}
+          @slotchange=${this.syncAriaAttributes}
+        ></slot>
+        <div
+          id="menu"
+          part="menu"
+          role="menu"
+          tabindex="-1"
+          aria-orientation="vertical"
+          @click=${this.handleMenuClick}
+          @submenu-opening=${this.handleSubmenuOpening}
+        >
+          <slot @slotchange=${this.handleMenuSlotChange}></slot>
+        </div>
+      </wa-popup>
+    `}};j.css=[et,Bi];s([z("slot:not([name])")],j.prototype,"defaultSlot",2);s([z("#menu")],j.prototype,"menu",2);s([z("wa-popup")],j.prototype,"popup",2);s([l({type:Boolean,reflect:!0})],j.prototype,"open",2);s([l({reflect:!0})],j.prototype,"size",2);s([S("size")],j.prototype,"handleSizeChange",1);s([l({reflect:!0})],j.prototype,"placement",2);s([l({type:Number})],j.prototype,"distance",2);s([l({type:Number})],j.prototype,"skidding",2);j=s([$("wa-dropdown")],j);var fr=w`
+  :host {
+    display: flex;
+    position: relative;
+    align-items: center;
+    padding: 0.5em 1em;
+    border-radius: var(--wa-border-radius-s);
+    isolation: isolate;
+    color: var(--wa-color-text-normal);
+    line-height: var(--wa-line-height-condensed);
+    cursor: pointer;
+    transition:
+      var(--wa-transition-fast) background-color var(--wa-transition-easing),
+      var(--wa-transition-fast) color var(--wa-transition-easing);
+  }
+
+  @media (hover: hover) {
+    :host(:hover:not(:state(disabled))) {
+      background-color: var(--wa-color-neutral-fill-normal);
+    }
+  }
+
+  :host(:state(submenu-open)) {
+    background-color: var(--wa-color-neutral-fill-normal);
+  }
+
+  :host(:focus-visible) {
+    z-index: 1;
+    outline: var(--wa-focus-ring);
+    background-color: var(--wa-color-neutral-fill-normal);
+  }
+
+  :host(:state(disabled)),
+  :host([disabled]) {
+    opacity: 0.5;
+    cursor: not-allowed;
+  }
+
+  /* Danger variant */
+  :host([variant='danger']),
+  :host([variant='danger']) #details {
+    color: var(--wa-color-danger-on-quiet);
+  }
+
+  @media (hover: hover) {
+    :host([variant='danger']:hover) {
+      background-color: var(--wa-color-danger-fill-normal);
+      color: var(--wa-color-danger-on-normal);
+    }
+  }
+
+  :host([variant='danger']:state(submenu-open)),
+  :host([variant='danger']:focus-visible) {
+    background-color: var(--wa-color-danger-fill-normal);
+    color: var(--wa-color-danger-on-normal);
+  }
+
+  :host([checkbox-adjacent]) {
+    padding-inline-start: 2em;
+  }
+
+  /* Only add padding when item actually has a submenu */
+  :host([submenu-adjacent]:not(:state(has-submenu))) #details {
+    padding-inline-end: 0;
+  }
+
+  :host(:state(has-submenu)[submenu-adjacent]) #details {
+    padding-inline-end: 1.75em;
+  }
+
+  #check {
+    visibility: hidden;
+    margin-inline-start: -1.5em;
+    margin-inline-end: 0.5em;
+    font-size: var(--wa-font-size-smaller);
+  }
+
+  :host(:state(checked)) #check {
+    visibility: visible;
+  }
+
+  #icon ::slotted(*) {
+    display: flex;
+    flex: 0 0 auto;
+    align-items: center;
+    margin-inline-end: 0.75em !important;
+    font-size: var(--wa-font-size-smaller);
+  }
+
+  #label {
+    flex: 1 1 auto;
+    min-width: 0;
+  }
+
+  #details {
+    display: flex;
+    flex: 0 0 auto;
+    align-items: center;
+    justify-content: end;
+    color: var(--wa-color-text-quiet);
+    font-size: var(--wa-font-size-smaller) !important;
+  }
+
+  #details ::slotted(*) {
+    margin-inline-start: 2em !important;
+  }
+
+  /* Submenu indicator icon */
+  #submenu-indicator {
+    position: absolute;
+    inset-inline-end: 1em;
+    color: var(--wa-color-neutral-on-quiet);
+    font-size: var(--wa-font-size-smaller);
+  }
+
+  /* Flip chevron icon when RTL */
+  :host(:dir(rtl)) #submenu-indicator {
+    transform: scaleX(-1);
+  }
+
+  /* Submenu styles */
+  #submenu {
+    display: flex;
+    z-index: 10;
+    position: absolute;
+    top: 0;
+    left: 0;
+    flex-direction: column;
+    width: max-content;
+    margin: 0;
+    padding: 0.25em;
+    border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
+    border-radius: var(--wa-border-radius-m);
+    background-color: var(--wa-color-surface-raised);
+    box-shadow: var(--wa-shadow-m);
+    color: var(--wa-color-text-normal);
+    text-align: start;
+    user-select: none;
+
+    /* Override default popover styles */
+    &[popover] {
+      margin: 0;
+      inset: auto;
+      padding: 0.25em;
+      overflow: visible;
+      border-radius: var(--wa-border-radius-m);
+    }
+
+    &.show {
+      animation: submenu-show var(--show-duration, var(--wa-transition-fast)) ease;
+    }
+
+    &.hide {
+      animation: submenu-show var(--show-duration, var(--wa-transition-fast)) ease reverse;
+    }
+
+    /* Submenu placement transform origins */
+    &[data-placement^='top'] {
+      transform-origin: bottom;
+    }
+
+    &[data-placement^='bottom'] {
+      transform-origin: top;
+    }
+
+    &[data-placement^='left'] {
+      transform-origin: right;
+    }
+
+    &[data-placement^='right'] {
+      transform-origin: left;
+    }
+
+    &[data-placement='left-start'] {
+      transform-origin: right top;
+    }
+
+    &[data-placement='left-end'] {
+      transform-origin: right bottom;
+    }
+
+    &[data-placement='right-start'] {
+      transform-origin: left top;
+    }
+
+    &[data-placement='right-end'] {
+      transform-origin: left bottom;
+    }
+
+    /* Safe triangle styling */
+    &::before {
+      display: none;
+      z-index: 9;
+      position: fixed;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+      background-color: transparent;
+      content: '';
+      clip-path: polygon(
+        var(--safe-triangle-cursor-x, 0) var(--safe-triangle-cursor-y, 0),
+        var(--safe-triangle-submenu-start-x, 0) var(--safe-triangle-submenu-start-y, 0),
+        var(--safe-triangle-submenu-end-x, 0) var(--safe-triangle-submenu-end-y, 0)
+      );
+      pointer-events: auto; /* Enable mouse events on the triangle */
+    }
+
+    &[data-visible]::before {
+      display: block;
+    }
+  }
+
+  ::slotted(wa-dropdown-item) {
+    font-size: inherit;
+  }
+
+  ::slotted(wa-divider) {
+    --spacing: 0.25em;
+  }
+
+  @keyframes submenu-show {
+    from {
+      scale: 0.9;
+      opacity: 0;
+    }
+    to {
+      scale: 1;
+      opacity: 1;
+    }
+  }
+`;var N=class extends P{constructor(){super(...arguments),this.hasSlotController=new tt(this,"[default]","start","end"),this.active=!1,this.variant="default",this.size="m",this.checkboxAdjacent=!1,this.submenuAdjacent=!1,this.type="normal",this.checked=!1,this.disabled=!1,this.submenuOpen=!1,this.hasSubmenu=!1,this.handleSlotChange=()=>{this.hasSubmenu=this.hasSlotController.test("submenu"),this.updateHasSubmenuState(),this.hasSubmenu?(this.setAttribute("aria-haspopup","menu"),this.setAt [...]
+      ${this.type==="checkbox"?g`
+            <wa-icon
+              id="check"
+              part="checkmark"
+              exportparts="svg:checkmark__svg"
+              library="system"
+              name="check"
+            ></wa-icon>
+          `:""}
+
+      <span id="icon" part="icon">
+        <slot name="icon"></slot>
+      </span>
+
+      <span id="label" part="label">
+        <slot></slot>
+      </span>
+
+      <span id="details" part="details">
+        <slot name="details"></slot>
+      </span>
+
+      ${this.hasSubmenu?g`
+            <wa-icon
+              id="submenu-indicator"
+              part="submenu-icon"
+              exportparts="svg:submenu-icon__svg"
+              library="system"
+              name="chevron-right"
+            ></wa-icon>
+          `:""}
+      ${this.hasSubmenu?g`
+            <div
+              id="submenu"
+              part="submenu"
+              popover="manual"
+              role="menu"
+              tabindex="-1"
+              aria-orientation="vertical"
+              hidden
+            >
+              <slot name="submenu"></slot>
+            </div>
+          `:""}
+    `}};N.css=fr;s([z("#submenu")],N.prototype,"submenuElement",2);s([l({type:Boolean})],N.prototype,"active",2);s([l({reflect:!0})],N.prototype,"variant",2);s([l({reflect:!0})],N.prototype,"size",2);s([S("size")],N.prototype,"handleSizeChange",1);s([l({attribute:"checkbox-adjacent",type:Boolean,reflect:!0})],N.prototype,"checkboxAdjacent",2);s([l({attribute:"submenu-adjacent",type:Boolean,reflect:!0})],N.prototype,"submenuAdjacent",2);s([l()],N.prototype,"value",2);s([l({reflect:!0})],N [...]
+  :host {
+    --arrow-color: black;
+    --arrow-size: var(--wa-tooltip-arrow-size);
+    --popup-border-width: 0px;
+    --show-duration: var(--wa-transition-fast);
+    --hide-duration: var(--wa-transition-fast);
+
+    /*
+     * These properties are computed to account for the arrow's dimensions after being rotated 45º. The constant
+     * 0.7071 is derived from sin(45) to calculate the length of the arrow after rotation.
+     *
+     * The diamond will be translated inward by --arrow-base-offset, the border thickness, to centralise it on
+     * the inner edge of the popup border. This also means we need to increase the size of the arrow by the
+     * same amount to compensate.
+     *
+     * A diamond shaped clipping mask is used to avoid overlap of popup content. This extends slightly inward so
+     * the popup border is covered with no sub-pixel rounding artifacts. The diamond corners are mitred at 22.5º
+     * to properly merge any arrow border with the popup border. The constant 1.4142 is derived from 1 + tan(22.5).
+     *
+     */
+    --arrow-base-offset: var(--popup-border-width);
+    --arrow-size-diagonal: calc((var(--arrow-size) + var(--arrow-base-offset)) * 0.7071);
+    --arrow-padding-offset: calc(var(--arrow-size-diagonal) - var(--arrow-size));
+    --arrow-size-div: calc(var(--arrow-size-diagonal) * 2);
+    --arrow-clipping-corner: calc(var(--arrow-base-offset) * 1.4142);
+
+    display: contents;
+  }
+
+  .popup {
+    position: absolute;
+    isolation: isolate;
+    max-width: var(--auto-size-available-width, none);
+    max-height: var(--auto-size-available-height, none);
+
+    /* Clear UA styles for [popover] */
+    :where(&) {
+      inset: unset;
+      padding: unset;
+      margin: unset;
+      width: unset;
+      height: unset;
+      color: unset;
+      background: unset;
+      border: unset;
+      overflow: unset;
+    }
+  }
+
+  .popup-fixed {
+    position: fixed;
+  }
+
+  .popup:not(.popup-active) {
+    display: none;
+  }
+
+  .arrow {
+    position: absolute;
+    width: var(--arrow-size-div);
+    height: var(--arrow-size-div);
+    background: var(--arrow-color);
+    z-index: 3;
+    clip-path: polygon(
+      var(--arrow-clipping-corner) 100%,
+      var(--arrow-base-offset) calc(100% - var(--arrow-base-offset)),
+      calc(var(--arrow-base-offset) - 2px) calc(100% - var(--arrow-base-offset)),
+      calc(100% - var(--arrow-base-offset)) calc(var(--arrow-base-offset) - 2px),
+      calc(100% - var(--arrow-base-offset)) var(--arrow-base-offset),
+      100% var(--arrow-clipping-corner),
+      100% 100%
+    );
+    rotate: 45deg;
+  }
+
+  :host([data-current-placement|='left']) .arrow {
+    rotate: -45deg;
+  }
+
+  :host([data-current-placement|='right']) .arrow {
+    rotate: 135deg;
+  }
+
+  :host([data-current-placement|='bottom']) .arrow {
+    rotate: 225deg;
+  }
+
+  /* Hover bridge */
+  .popup-hover-bridge:not(.popup-hover-bridge-visible) {
+    display: none;
+  }
+
+  .popup-hover-bridge {
+    position: fixed;
+    z-index: 899;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    clip-path: polygon(
+      var(--hover-bridge-top-left-x, 0) var(--hover-bridge-top-left-y, 0),
+      var(--hover-bridge-top-right-x, 0) var(--hover-bridge-top-right-y, 0),
+      var(--hover-bridge-bottom-right-x, 0) var(--hover-bridge-bottom-right-y, 0),
+      var(--hover-bridge-bottom-left-x, 0) var(--hover-bridge-bottom-left-y, 0)
+    );
+  }
+
+  /* Built-in animations */
+  .show {
+    animation: show var(--show-duration) ease;
+  }
+
+  .hide {
+    animation: show var(--hide-duration) ease reverse;
+  }
+
+  @keyframes show {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+
+  .show-with-scale {
+    animation: show-with-scale var(--show-duration) ease;
+  }
+
+  .hide-with-scale {
+    animation: show-with-scale var(--hide-duration) ease reverse;
+  }
+
+  @keyframes show-with-scale {
+    from {
+      opacity: 0;
+      scale: 0.8;
+    }
+    to {
+      opacity: 1;
+      scale: 1;
+    }
+  }
+`;function vr(t){return qa(t)}function Do(t){return t.assignedSlot?t.assignedSlot:t.parentNode instanceof ShadowRoot?t.parentNode.host:t.parentNode}function qa(t){for(let e=t;e;e=Do(e))if(e instanceof Element&&getComputedStyle(e).display==="none")return null;for(let e=Do(t);e;e=Do(e)){if(!(e instanceof Element))continue;let o=getComputedStyle(e);if(o.display!=="contents"&&(o.position!=="static"||ie(o)||e.tagName==="BODY"))return e}return null}function wr(t){return t!==null&&typeof t=="ob [...]
+      <slot name="anchor" @slotchange=${this.handleAnchorChange}></slot>
+
+      <span
+        part="hover-bridge"
+        class=${M({"popup-hover-bridge":!0,"popup-hover-bridge-visible":this.hoverBridge&&this.active})}
+      ></span>
+
+      <div
+        popover="manual"
+        part="popup"
+        class=${M({popup:!0,"popup-active":this.active,"popup-fixed":!this.SUPPORTS_POPOVER,"popup-has-arrow":this.arrow})}
+      >
+        <slot></slot>
+        ${this.arrow?g`<div part="arrow" class="arrow" role="presentation"></div>`:""}
+      </div>
+    `}};O.css=gr;s([z(".popup")],O.prototype,"popup",2);s([z(".arrow")],O.prototype,"arrowEl",2);s([l({attribute:!1,type:Boolean})],O.prototype,"SUPPORTS_POPOVER",2);s([l()],O.prototype,"anchor",2);s([l({type:Boolean,reflect:!0})],O.prototype,"active",2);s([l({reflect:!0})],O.prototype,"placement",2);s([l()],O.prototype,"boundary",2);s([l({type:Number})],O.prototype,"distance",2);s([l({type:Number})],O.prototype,"skidding",2);s([l({type:Boolean})],O.prototype,"arrow",2);s([l({attribute:" [...]
+  :host {
+    border-width: 0;
+  }
+
+  :host(:focus) {
+    outline: none;
+  }
+
+  .text-field {
+    display: flex;
+    align-items: stretch;
+    justify-content: start;
+    position: relative;
+    transition: inherit;
+    height: var(--wa-form-control-height);
+    border-color: var(--wa-form-control-border-color);
+    border-radius: var(--wa-form-control-border-radius);
+    border-style: var(--wa-form-control-border-style);
+    border-width: var(--wa-form-control-border-width);
+    cursor: text;
+    color: var(--wa-form-control-value-color);
+    font-size: var(--wa-form-control-value-font-size);
+    font-family: inherit;
+    font-weight: var(--wa-form-control-value-font-weight);
+    line-height: var(--wa-form-control-value-line-height);
+    vertical-align: middle;
+    width: 100%;
+    transition:
+      background-color var(--wa-transition-normal),
+      border-color var(--wa-transition-normal),
+      outline-color var(--wa-transition-fast);
+    transition-timing-function: var(--wa-transition-easing);
+    background-color: var(--wa-form-control-background-color);
+    box-shadow: var(--box-shadow);
+    padding: 0 var(--wa-form-control-padding-inline);
+    outline: var(--wa-focus-ring-style) var(--wa-focus-ring-width) transparent;
+    outline-offset: var(--wa-focus-ring-offset);
+
+    &:focus-within {
+      outline-color: var(--wa-color-focus);
+    }
+
+    /* Style disabled inputs */
+    &:has(:disabled) {
+      cursor: not-allowed;
+      opacity: 0.5;
+    }
+  }
+
+  /* Appearance modifiers */
+  :host([appearance='outlined']) .text-field {
+    background-color: var(--wa-form-control-background-color);
+    border-color: var(--wa-form-control-border-color);
+  }
+
+  :host([appearance='filled']) .text-field {
+    background-color: var(--wa-color-neutral-fill-quiet);
+    border-color: var(--wa-color-neutral-fill-quiet);
+  }
+
+  :host([appearance='filled-outlined']) .text-field {
+    background-color: var(--wa-color-neutral-fill-quiet);
+    border-color: var(--wa-form-control-border-color);
+  }
+
+  :host([pill]) .text-field {
+    border-radius: var(--wa-border-radius-pill) !important;
+  }
+
+  .text-field {
+    /* Show autofill styles over the entire text field, not just the native <input> */
+    &:has(:autofill),
+    &:has(:-webkit-autofill) {
+      background-color: var(--wa-color-brand-fill-quiet) !important;
+    }
+
+    input,
+    textarea {
+      /*
+      Fixes an alignment issue with placeholders.
+      https://github.com/shoelace-style/webawesome/issues/342
+    */
+      height: 100%;
+
+      padding: 0;
+      border: none;
+      outline: none;
+      box-shadow: none;
+      margin: 0;
+      cursor: inherit;
+      -webkit-appearance: none;
+      font: inherit;
+
+      /* Turn off Safari's autofill styles */
+      &:-webkit-autofill,
+      &:-webkit-autofill:hover,
+      &:-webkit-autofill:focus,
+      &:-webkit-autofill:active {
+        -webkit-background-clip: text;
+        background-color: transparent;
+        -webkit-text-fill-color: inherit;
+      }
+    }
+  }
+
+  input {
+    flex: 1 1 auto;
+    min-width: 0;
+    height: 100%;
+    transition: inherit;
+
+    /* prettier-ignore */
+    background-color: rgb(118 118 118 / 0); /* ensures proper placeholder styles in webkit's date input */
+    height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
+    padding-block: 0;
+    color: inherit;
+
+    &:autofill {
+      &,
+      &:hover,
+      &:focus,
+      &:active {
+        box-shadow: none;
+        caret-color: var(--wa-form-control-value-color);
+      }
+    }
+
+    &::placeholder {
+      color: var(--wa-form-control-placeholder-color);
+      user-select: none;
+      -webkit-user-select: none;
+    }
+
+    &::-webkit-search-decoration,
+    &::-webkit-search-cancel-button,
+    &::-webkit-search-results-button,
+    &::-webkit-search-results-decoration {
+      -webkit-appearance: none;
+    }
+
+    &:focus {
+      outline: none;
+    }
+  }
+
+  textarea {
+    &:autofill {
+      &,
+      &:hover,
+      &:focus,
+      &:active {
+        box-shadow: none;
+        caret-color: var(--wa-form-control-value-color);
+      }
+    }
+
+    &::placeholder {
+      color: var(--wa-form-control-placeholder-color);
+      user-select: none;
+      -webkit-user-select: none;
+    }
+  }
+
+  .start,
+  .end {
+    display: inline-flex;
+    flex: 0 0 auto;
+    align-items: center;
+    cursor: default;
+
+    &::slotted(wa-icon) {
+      color: var(--wa-color-neutral-on-quiet);
+    }
+  }
+
+  .start::slotted(*) {
+    margin-inline-end: var(--wa-form-control-padding-inline);
+  }
+
+  .end::slotted(*) {
+    margin-inline-start: var(--wa-form-control-padding-inline);
+  }
+
+  /*
+   * Clearable + Password Toggle
+   */
+
+  .clear,
+  .password-toggle {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    font-size: inherit;
+    color: var(--wa-color-neutral-on-quiet);
+    border: none;
+    background: none;
+    padding: 0;
+    transition: var(--wa-transition-normal) color;
+    cursor: pointer;
+    margin-inline-start: var(--wa-form-control-padding-inline);
+
+    @media (hover: hover) {
+      &:hover {
+        color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
+      }
+    }
+
+    &:active {
+      color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
+    }
+
+    &:focus {
+      outline: none;
+    }
+  }
+
+  /* Don't show the browser's password toggle in Edge */
+  ::-ms-reveal {
+    display: none;
+  }
+
+  /* Hide the built-in number spinner */
+  :host([without-spin-buttons]) input[type='number'] {
+    -moz-appearance: textfield;
+
+    &::-webkit-outer-spin-button,
+    &::-webkit-inner-spin-button {
+      -webkit-appearance: none;
+      display: none;
+    }
+  }
+`;var ne=w`
+  :host {
+    display: flex;
+    flex-direction: column;
+  }
+
+  /* Treat wrapped labels, inputs, and hints as direct children of the host element */
+  [part~='form-control'] {
+    display: contents;
+  }
+
+  /* Label */
+  :is([part~='form-control-label'], [part~='label']):has(*:not(:empty)),
+  :is([part~='form-control-label'], [part~='label']).has-label {
+    display: inline-flex;
+    color: var(--wa-form-control-label-color);
+    font-weight: var(--wa-form-control-label-font-weight);
+    line-height: var(--wa-form-control-label-line-height);
+    margin-block-end: 0.5em;
+  }
+
+  :host([required]) :is([part~='form-control-label'], [part~='label'])::after {
+    content: var(--wa-form-control-required-content);
+    margin-inline-start: var(--wa-form-control-required-content-offset);
+    color: var(--wa-form-control-required-content-color);
+  }
+
+  /* Help text */
+  [part~='hint'] {
+    display: block;
+    color: var(--wa-form-control-hint-color);
+    font-weight: var(--wa-form-control-hint-font-weight);
+    line-height: var(--wa-form-control-hint-line-height);
+    margin-block-start: 0.5em;
+    font-size: var(--wa-font-size-smaller);
+
+    &:not(.has-slotted, .has-hint) {
+      display: none;
+    }
+  }
+`;var no=Gt(class extends kt{constructor(t){if(super(t),t.type!==at.PROPERTY&&t.type!==at.ATTRIBUTE&&t.type!==at.BOOLEAN_ATTRIBUTE)throw Error("The `live` directive is not allowed on child or event bindings");if(!Pi(t))throw Error("`live` bindings can only contain a single expression")}render(t){return t}update(t,[e]){if(e===W||e===T)return e;let o=t.element,i=t.name;if(t.type===at.PROPERTY){if(e===o[i])return W}else if(t.type===at.BOOLEAN_ATTRIBUTE){if(!!e===o.hasAttribute(i))return W}e [...]
+      <label
+        part="form-control-label label"
+        class=${M({label:!0,"has-label":o})}
+        for="input"
+        aria-hidden=${o?"false":"true"}
+      >
+        <slot name="label">${this.label}</slot>
+      </label>
+
+      <div part="base input-wrapper" class="text-field">
+        <slot name="start" part="start" class="start"></slot>
+
+        <input
+          part="input"
+          id="input"
+          class="control"
+          type=${this.type==="password"&&this.passwordVisible?"text":this.type}
+          title=${this.title}
+          name=${k(this.name)}
+          ?disabled=${this.disabled}
+          ?readonly=${this.readonly}
+          ?required=${this.required}
+          placeholder=${k(this.placeholder)}
+          minlength=${k(this.minlength)}
+          maxlength=${k(this.maxlength)}
+          min=${k(this.min)}
+          max=${k(this.max)}
+          step=${k(this.step)}
+          .value=${no(this.value??"")}
+          autocapitalize=${k(this.autocapitalize)}
+          autocomplete=${k(this.autocomplete)}
+          autocorrect=${this.autocorrect?"on":"off"}
+          ?autofocus=${this.autofocus}
+          spellcheck=${this.spellcheck}
+          pattern=${k(this.pattern)}
+          enterkeyhint=${k(this.enterkeyhint)}
+          inputmode=${k(this.inputmode)}
+          aria-describedby="hint"
+          @change=${this.handleChange}
+          @input=${this.handleInput}
+          @keydown=${this.handleKeyDown}
+        />
+
+        ${a?g`
+              <button
+                part="clear-button"
+                class="clear"
+                type="button"
+                aria-label=${this.localize.term("clearEntry")}
+                @click=${this.handleClearClick}
+                tabindex="-1"
+              >
+                <slot name="clear-icon">
+                  <wa-icon name="circle-xmark" library="system" variant="regular"></wa-icon>
+                </slot>
+              </button>
+            `:""}
+        ${this.passwordToggle&&!this.disabled?g`
+              <button
+                part="password-toggle-button"
+                class="password-toggle"
+                type="button"
+                aria-label=${this.localize.term(this.passwordVisible?"hidePassword":"showPassword")}
+                @click=${this.handlePasswordToggle}
+                tabindex="-1"
+              >
+                ${this.passwordVisible?g`
+                      <slot name="hide-password-icon">
+                        <wa-icon name="eye-slash" library="system" variant="regular"></wa-icon>
+                      </slot>
+                    `:g`
+                      <slot name="show-password-icon">
+                        <wa-icon name="eye" library="system" variant="regular"></wa-icon>
+                      </slot>
+                    `}
+              </button>
+            `:""}
+
+        <slot name="end" part="end" class="end"></slot>
+      </div>
+
+      <slot
+        id="hint"
+        part="hint"
+        name="hint"
+        class=${M({"has-slotted":i})}
+        aria-hidden=${i?"false":"true"}
+        >${this.hint}</slot
+      >
+    `}};y.css=[et,ne,Cr];y.shadowRootOptions={...V.shadowRootOptions,delegatesFocus:!0};s([z("input")],y.prototype,"input",2);s([l()],y.prototype,"title",2);s([l({reflect:!0})],y.prototype,"type",2);s([B()],y.prototype,"value",1);s([l({attribute:"value",reflect:!0})],y.prototype,"defaultValue",2);s([l({reflect:!0})],y.prototype,"size",2);s([S("size")],y.prototype,"handleSizeChange",1);s([l({reflect:!0})],y.prototype,"appearance",2);s([l({type:Boolean,reflect:!0})],y.prototype,"pill",2);s [...]
+  :host {
+    --current-text-color: var(--wa-color-brand-on-loud);
+
+    display: block;
+    color: var(--wa-color-text-normal);
+    -webkit-user-select: none;
+    user-select: none;
+
+    position: relative;
+    display: flex;
+    align-items: center;
+    font: inherit;
+    padding: 0.5em 1em 0.5em 0.25em;
+    border-radius: var(--wa-border-radius-s);
+    line-height: var(--wa-line-height-condensed);
+    transition: var(--wa-transition-fast) background-color var(--wa-transition-easing);
+    cursor: pointer;
+  }
+
+  :host(:focus) {
+    outline: none;
+  }
+
+  @media (hover: hover) {
+    :host(:not(:state(disabled), :state(current)):is(:state(hover), :hover)) {
+      background-color: var(--wa-color-neutral-fill-normal);
+      color: var(--wa-color-neutral-on-normal);
+    }
+  }
+
+  :host(:state(current)),
+  :host(:state(disabled):state(current)) {
+    background-color: var(--wa-form-control-activated-color);
+    color: var(--current-text-color);
+    opacity: 1;
+  }
+
+  :host(:state(disabled)) {
+    outline: none;
+    opacity: 0.5;
+    cursor: not-allowed;
+  }
+
+  .label {
+    flex: 1 1 auto;
+    display: inline-block;
+  }
+
+  .check {
+    flex: 0 0 auto;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: var(--wa-font-size-smaller);
+    visibility: hidden;
+    width: 2em;
+  }
+
+  :host(:state(selected)) .check {
+    visibility: visible;
+  }
+
+  .start,
+  .end {
+    flex: 0 0 auto;
+    display: flex;
+    align-items: center;
+  }
+
+  .start::slotted(*) {
+    margin-inline-end: 0.5em;
+  }
+
+  .end::slotted(*) {
+    margin-inline-start: 0.5em;
+  }
+
+  @media (forced-colors: active) {
+    :host(:hover:not([aria-disabled='true'])) {
+      outline: dashed 1px SelectedItem;
+      outline-offset: -1px;
+    }
+  }
+`;function Ae(t,e=0){if(!t||!globalThis.Node)return"";if(typeof t[Symbol.iterator]=="function")return(Array.isArray(t)?t:[...t]).map(r=>Ae(r,--e)).join("");let o=t;if(o.nodeType===Node.TEXT_NODE)return o.textContent??"";if(o.nodeType===Node.ELEMENT_NODE){let i=o;if(i.hasAttribute("slot")||i.matches("style, script"))return"";if(i instanceof HTMLSlotElement){let r=i.assignedNodes({flatten:!0});if(r.length>0)return Ae(r,--e)}return e>-1?Ae(i,--e):i.textContent??""}return o.hasChildNodes()?A [...]
+      ${t?g`<wa-icon
+            part="checked-icon"
+            class="check"
+            name="check"
+            library="system"
+            variant="solid"
+            aria-hidden="true"
+          ></wa-icon>`:g`<span part="checked-icon" class="check" aria-hidden="true"></span>`}
+      <slot part="start" name="start" class="start"></slot>
+      <slot part="label" class="label" @slotchange=${this.handleDefaultSlotChange}></slot>
+      <slot part="end" name="end" class="end"></slot>
+    `}};ut.css=xr;s([z(".label")],ut.prototype,"defaultSlot",2);s([B()],ut.prototype,"current",2);s([l({reflect:!0})],ut.prototype,"value",2);s([l({type:Boolean})],ut.prototype,"disabled",2);s([l({type:Boolean,attribute:!1})],ut.prototype,"selected",2);s([l({type:Boolean,attribute:"selected"})],ut.prototype,"defaultSelected",2);s([l()],ut.prototype,"label",1);ut=s([$("wa-option")],ut);var Sr=w`
+  :host {
+    --tag-max-size: 10ch;
+    --show-duration: var(--wa-transition-fast);
+    --hide-duration: var(--wa-transition-fast);
+  }
+
+  /* Add ellipses to multi select options */
+  :host wa-tag::part(content) {
+    display: initial;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    max-width: var(--tag-max-size);
+  }
+
+  :host .disabled [part~='combobox'] {
+    opacity: 0.5;
+    cursor: not-allowed;
+    outline: none;
+  }
+
+  :host .enabled:is(.open, :focus-within) [part~='combobox'] {
+    outline-color: var(--wa-color-focus);
+  }
+
+  /** The popup */
+  .select {
+    flex: 1 1 auto;
+    display: inline-flex;
+    width: 100%;
+    position: relative;
+    vertical-align: middle;
+
+    /* Pass through from select to the popup */
+    --show-duration: inherit;
+    --hide-duration: inherit;
+
+    &::part(popup) {
+      z-index: 900;
+    }
+
+    &[data-current-placement^='top']::part(popup) {
+      transform-origin: bottom;
+    }
+
+    &[data-current-placement^='bottom']::part(popup) {
+      transform-origin: top;
+    }
+  }
+
+  /* Combobox */
+  .combobox {
+    flex: 1;
+    display: flex;
+    width: 100%;
+    min-width: 0;
+    align-items: center;
+    justify-content: start;
+
+    min-height: var(--wa-form-control-height);
+
+    background-color: var(--wa-form-control-background-color);
+    border-color: var(--wa-form-control-border-color);
+    border-radius: var(--wa-form-control-border-radius);
+    border-style: var(--wa-form-control-border-style);
+    border-width: var(--wa-form-control-border-width);
+    color: var(--wa-form-control-value-color);
+    cursor: pointer;
+    font-family: inherit;
+    font-weight: var(--wa-form-control-value-font-weight);
+    line-height: var(--wa-form-control-value-line-height);
+    overflow: hidden;
+    padding: 0 var(--wa-form-control-padding-inline);
+    position: relative;
+    vertical-align: middle;
+    transition:
+      background-color var(--wa-transition-normal),
+      border-color var(--wa-transition-normal),
+      outline-color var(--wa-transition-fast);
+    transition-timing-function: var(--wa-transition-easing);
+    outline: var(--wa-focus-ring-style) var(--wa-focus-ring-width) transparent;
+    outline-offset: var(--wa-focus-ring-offset);
+
+    /* Pills */
+    :host([pill]) & {
+      border-radius: var(--wa-border-radius-pill);
+    }
+  }
+
+  /* Appearance modifiers */
+  :host([appearance='outlined']) .combobox {
+    background-color: var(--wa-form-control-background-color);
+    border-color: var(--wa-form-control-border-color);
+  }
+
+  :host([appearance='filled']) .combobox {
+    background-color: var(--wa-color-neutral-fill-quiet);
+    border-color: var(--wa-color-neutral-fill-quiet);
+  }
+
+  :host([appearance='filled-outlined']) .combobox {
+    background-color: var(--wa-color-neutral-fill-quiet);
+    border-color: var(--wa-form-control-border-color);
+  }
+
+  .display-input {
+    position: relative;
+    width: 100%;
+    font: inherit;
+    border: none;
+    background: none;
+    line-height: var(--wa-form-control-value-line-height);
+    color: var(--wa-form-control-value-color);
+    cursor: inherit;
+    overflow: hidden;
+    padding: 0;
+    margin: 0;
+    -webkit-appearance: none;
+
+    &:focus {
+      outline: none;
+    }
+
+    &::placeholder {
+      color: var(--wa-form-control-placeholder-color);
+    }
+  }
+
+  /* Manage spacing when tags are present */
+  :host([multiple]) {
+    --_padding-with-tags: calc(var(--wa-form-control-height) * 0.1 - var(--wa-form-control-border-width));
+
+    & .combobox:has(.tags wa-tag) {
+      padding-block: var(--_padding-with-tags);
+      padding-inline-start: var(--_padding-with-tags);
+    }
+  }
+
+  /* Visually hide the display input when multiple is enabled */
+  :host([multiple]) .combobox:has(.tags wa-tag) .display-input {
+    position: absolute;
+    z-index: -1;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    opacity: 0;
+  }
+
+  .value-input {
+    position: absolute;
+    z-index: -1;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    opacity: 0;
+    padding: 0;
+    margin: 0;
+  }
+
+  .tags {
+    display: flex;
+    flex: 1;
+    align-items: center;
+    flex-wrap: wrap;
+    gap: 0.25em;
+
+    &::slotted(wa-tag) {
+      cursor: pointer !important;
+    }
+
+    .disabled &,
+    .disabled &::slotted(wa-tag) {
+      cursor: not-allowed !important;
+    }
+  }
+
+  /* Start and End */
+
+  .start,
+  .end {
+    flex: 0;
+    display: inline-flex;
+    align-items: center;
+    color: var(--wa-color-neutral-on-quiet);
+  }
+
+  .end::slotted(*) {
+    margin-inline-start: var(--wa-form-control-padding-inline);
+  }
+
+  .start::slotted(*) {
+    margin-inline-end: var(--wa-form-control-padding-inline);
+  }
+
+  :host([multiple]) .combobox:has(.tags wa-tag) .start::slotted(*) {
+    margin-inline-start: calc(var(--wa-form-control-padding-inline) - var(--_padding-with-tags));
+  }
+
+  /* Clear button */
+  [part~='clear-button'] {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    font-size: inherit;
+    color: var(--wa-color-neutral-on-quiet);
+    border: none;
+    background: none;
+    padding: 0;
+    transition: color var(--wa-transition-normal);
+    cursor: pointer;
+    margin-inline-start: var(--wa-form-control-padding-inline);
+
+    &:focus {
+      outline: none;
+    }
+
+    @media (hover: hover) {
+      &:hover {
+        color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
+      }
+    }
+
+    &:active {
+      color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
+    }
+  }
+
+  /* Expand icon */
+  .expand-icon {
+    flex: 0 0 auto;
+    display: flex;
+    align-items: center;
+    color: var(--wa-color-neutral-on-quiet);
+    transition: rotate var(--wa-transition-slow) var(--wa-transition-easing);
+    rotate: 0deg;
+    margin-inline-start: var(--wa-form-control-padding-inline);
+
+    .open & {
+      rotate: -180deg;
+    }
+  }
+
+  /* Listbox */
+  .listbox {
+    display: block;
+    position: relative;
+    font: inherit;
+    box-shadow: var(--wa-shadow-m);
+    background: var(--wa-color-surface-raised);
+    border-color: var(--wa-color-surface-border);
+    border-radius: var(--wa-border-radius-m);
+    border-style: var(--wa-border-style);
+    border-width: var(--wa-border-width-s);
+    padding: 0.25em;
+    overflow: auto;
+    overscroll-behavior: none;
+
+    /* Make sure it adheres to the popup's auto size */
+    max-width: var(--auto-size-available-width);
+    max-height: var(--auto-size-available-height);
+
+    &::slotted(wa-divider) {
+      --spacing: 0.5em;
+    }
+  }
+
+  /* Space options with half the listbox's padding */
+  .listbox slot:not([name]) {
+    display: flex;
+    flex-direction: column;
+    gap: 0.125em;
+  }
+
+  slot:not([name])::slotted(small) {
+    display: block;
+    font-size: var(--wa-font-size-smaller);
+    font-weight: var(--wa-font-weight-semibold);
+    color: var(--wa-color-text-quiet);
+    padding-block: 0.5em;
+    padding-inline: 2.25em;
+  }
+`;var Lr=(t={})=>{let{validationElement:e,validationProperty:o}=t;e||typeof document<"u"&&"createElement"in document&&(e=Object.assign(document.createElement("input"),{required:!0})),o||(o="value");let i={observedAttributes:["required"],message:e?.validationMessage,checkValidity(r){let a={message:"",isValid:!0,invalidKeys:[]};return(r.required??r.hasAttribute("required"))&&!r[o]&&(a.message=typeof i.message=="function"?i.message(r):i.message||"",a.isValid=!1,a.invalidKeys.push("valueMiss [...]
+        <wa-tag
+          part="tag"
+          exportparts="
+            base:tag__base,
+            content:tag__content,
+            remove-button:tag__remove-button,
+            remove-button__base:tag__remove-button__base
+          "
+          ?pill=${this.pill}
+          size=${this.size}
+          with-remove
+          data-value=${t.value}
+          @wa-remove=${e=>this.handleTagRemove(e,t)}
+        >
+          ${t.label}
+        </wa-tag>
+      `,this.handleDocumentFocusIn=t=>{let e=t.composedPath();this&&!e.includes(this)&&this.hide()},this.handleDocumentKeyDown=t=>{let e=t.target,o=e.closest('[part~="clear-button"]')!==null,i=e.closest("wa-button")!==null;if(!(o||i)){if(t.key==="Escape"&&this.open&&qt(this)&&(t.preventDefault(),t.stopPropagation(),this.hide(),this.displayInput.focus({preventScroll:!0})),t.key==="Enter"||t.key===" "&&this.typeToSelectString===""){if(t.preventDefault(),t.stopImmediatePropagation(),!this.o [...]
+          <wa-tag
+            part="tag"
+            exportparts="
+              base:tag__base,
+              content:tag__content,
+              remove-button:tag__remove-button,
+              remove-button__base:tag__remove-button__base
+            "
+            >+${this.selectedOptions.length-e}</wa-tag
+          >
+        `;return null})}updated(t){super.updated(t),(t.has("value")||t.has("displayLabel"))&&this.customStates.set("blank",!this.value&&!this.displayLabel)}handleDisabledChange(){this.disabled&&this.open&&(this.open=!1)}handleValueChange(){let t=this.getAllOptions(),e=Array.isArray(this.value)?this.value:[this.value],o=t.filter(i=>e.includes(i.value));this.setSelectedOptions(o),this.updateValidity()}async handleOpenChange(){if(this.open&&!this.disabled){this.setCurrentOption(this.selecte [...]
+      <div
+        part="form-control"
+        class=${M({"form-control":!0,"form-control-has-label":o})}
+      >
+        <label
+          id="label"
+          part="form-control-label label"
+          class=${M({label:!0,"has-label":o})}
+          aria-hidden=${o?"false":"true"}
+          @click=${this.handleLabelClick}
+        >
+          <slot name="label">${this.label}</slot>
+        </label>
+
+        <div part="form-control-input" class="form-control-input">
+          <wa-popup
+            class=${M({select:!0,open:this.open,disabled:this.disabled,enabled:!this.disabled,multiple:this.multiple})}
+            placement=${this.placement}
+            flip
+            shift
+            sync="width"
+            auto-size="vertical"
+            auto-size-padding="10"
+          >
+            <div
+              part="combobox"
+              class="combobox"
+              slot="anchor"
+              @keydown=${this.handleComboboxKeyDown}
+              @mousedown=${this.handleComboboxMouseDown}
+              @click=${this.handleComboboxClick}
+            >
+              <slot part="start" name="start" class="start"></slot>
+
+              <input
+                part="display-input"
+                class="display-input"
+                type="text"
+                placeholder=${this.placeholder}
+                .disabled=${this.disabled}
+                .value=${this.displayLabel}
+                ?required=${this.required}
+                autocomplete="off"
+                spellcheck="false"
+                autocapitalize="off"
+                readonly
+                aria-invalid=${!this.validity.valid}
+                aria-controls="listbox"
+                aria-expanded=${this.open?"true":"false"}
+                aria-haspopup="listbox"
+                aria-labelledby="label"
+                aria-disabled=${this.disabled?"true":"false"}
+                aria-describedby="hint"
+                role="combobox"
+                tabindex="0"
+                @focus=${this.handleFocus}
+              />
+
+              <!-- Tags need to wait for first hydration before populating otherwise it will create a hydration mismatch. -->
+              ${this.multiple&&this.hasUpdated?g`<div part="tags" class="tags" @wa-remove=${this.handleTagRemove}>${this.tags}</div>`:""}
+
+              <input
+                class="value-input"
+                type="text"
+                ?disabled=${this.disabled}
+                ?required=${this.required}
+                .value=${Array.isArray(this.value)?this.value.join(", "):this.value}
+                tabindex="-1"
+                aria-hidden="true"
+                @focus=${()=>this.focus()}
+              />
+
+              ${r?g`
+                    <button
+                      part="clear-button"
+                      type="button"
+                      aria-label=${this.localize.term("clearEntry")}
+                      @mousedown=${this.handleClearMouseDown}
+                      @click=${this.handleClearClick}
+                      tabindex="-1"
+                    >
+                      <slot name="clear-icon">
+                        <wa-icon name="circle-xmark" library="system" variant="regular"></wa-icon>
+                      </slot>
+                    </button>
+                  `:""}
+
+              <slot name="end" part="end" class="end"></slot>
+
+              <slot name="expand-icon" part="expand-icon" class="expand-icon">
+                <wa-icon library="system" name="chevron-down" variant="solid"></wa-icon>
+              </slot>
+            </div>
+
+            <div
+              id="listbox"
+              role="listbox"
+              aria-expanded=${this.open?"true":"false"}
+              aria-multiselectable=${this.multiple?"true":"false"}
+              aria-labelledby="label"
+              part="listbox"
+              class="listbox"
+              tabindex="-1"
+              @mouseup=${this.handleOptionClick}
+            >
+              <slot @slotchange=${this.handleDefaultSlotChange}></slot>
+            </div>
+          </wa-popup>
+        </div>
+
+        <slot
+          id="hint"
+          name="hint"
+          part="hint"
+          class=${M({"has-slotted":i})}
+          aria-hidden=${i?"false":"true"}
+          >${this.hint}</slot
+        >
+      </div>
+    `}};A.css=[Sr,ne,et];s([z(".select")],A.prototype,"popup",2);s([z(".combobox")],A.prototype,"combobox",2);s([z(".display-input")],A.prototype,"displayInput",2);s([z(".value-input")],A.prototype,"valueInput",2);s([z(".listbox")],A.prototype,"listbox",2);s([B()],A.prototype,"displayLabel",2);s([B()],A.prototype,"currentOption",2);s([B()],A.prototype,"selectedOptions",2);s([l({reflect:!0})],A.prototype,"name",2);s([l({attribute:!1})],A.prototype,"defaultValue",1);s([l({attribute:"value" [...]
+  @layer wa-component {
+    :host {
+      display: inline-flex;
+      gap: 0.5em;
+      border-radius: var(--wa-border-radius-m);
+      align-items: center;
+      background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+      border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
+      border-style: var(--wa-border-style);
+      border-width: var(--wa-border-width-s);
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      font-size: inherit;
+      line-height: 1;
+      white-space: nowrap;
+      user-select: none;
+      -webkit-user-select: none;
+      height: calc(var(--wa-form-control-height) * 0.8);
+      line-height: calc(var(--wa-form-control-height) - var(--wa-form-control-border-width) * 2);
+      padding: 0 0.75em;
+    }
+
+    /* Appearance modifiers */
+    :host([appearance='outlined']) {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: transparent;
+      border-color: var(--wa-color-border-loud, var(--wa-color-neutral-border-loud));
+    }
+
+    :host([appearance='filled']) {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+      border-color: transparent;
+    }
+
+    :host([appearance='filled-outlined']) {
+      color: var(--wa-color-on-quiet, var(--wa-color-neutral-on-quiet));
+      background-color: var(--wa-color-fill-quiet, var(--wa-color-neutral-fill-quiet));
+      border-color: var(--wa-color-border-normal, var(--wa-color-neutral-border-normal));
+    }
+
+    :host([appearance='accent']) {
+      color: var(--wa-color-on-loud, var(--wa-color-neutral-on-loud));
+      background-color: var(--wa-color-fill-loud, var(--wa-color-neutral-fill-loud));
+      border-color: transparent;
+    }
+  }
+
+  .content {
+    font-size: var(--wa-font-size-smaller);
+  }
+
+  [part='remove-button'] {
+    line-height: 1;
+  }
+
+  [part='remove-button']::part(base) {
+    padding: 0;
+    height: 1em;
+    width: 1em;
+    color: currentColor;
+  }
+
+  @media (hover: hover) {
+    :host(:hover) > [part='remove-button']::part(base) {
+      background-color: transparent;
+      color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
+    }
+  }
+
+  :host(:active) > [part='remove-button']::part(base) {
+    background-color: transparent;
+    color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
+  }
+
+  /*
+   * Pill modifier
+   */
+  :host([pill]) {
+    border-radius: var(--wa-border-radius-pill);
+  }
+`;var St=class extends P{constructor(){super(...arguments),this.localize=new I(this),this.variant="neutral",this.appearance="filled-outlined",this.size="m",this.pill=!1,this.withRemove=!1}handleSizeChange(){G(this.localName,this.size)}handleRemoveClick(){this.dispatchEvent(new kr)}render(){return g`
+      <slot part="content" class="content"></slot>
+
+      ${this.withRemove?g`
+            <wa-button
+              part="remove-button"
+              exportparts="base:remove-button__base"
+              class="remove"
+              appearance="plain"
+              size=${this.size}
+              @click=${this.handleRemoveClick}
+              tabindex="-1"
+            >
+              <wa-icon name="xmark" library="system" variant="solid" label=${this.localize.term("remove")}></wa-icon>
+            </wa-button>
+          `:""}
+    `}};St.css=[Er,Be,et];s([l({reflect:!0})],St.prototype,"variant",2);s([l({reflect:!0})],St.prototype,"appearance",2);s([l({reflect:!0})],St.prototype,"size",2);s([S("size")],St.prototype,"handleSizeChange",1);s([l({type:Boolean,reflect:!0})],St.prototype,"pill",2);s([l({attribute:"with-remove",type:Boolean})],St.prototype,"withRemove",2);St=s([$("wa-tag")],St);var zr=w`
+  :host {
+    display: inline-block;
+    color: var(--wa-color-neutral-on-quiet);
+    font-weight: var(--wa-font-weight-action);
+  }
+
+  .tab {
+    display: inline-flex;
+    align-items: center;
+    font: inherit;
+    padding: 1em 1.5em;
+    white-space: nowrap;
+    user-select: none;
+    -webkit-user-select: none;
+    cursor: pointer;
+    transition: color var(--wa-transition-fast) var(--wa-transition-easing);
+
+    ::slotted(wa-icon:first-child) {
+      margin-inline-end: 0.5em;
+    }
+
+    ::slotted(wa-icon:last-child) {
+      margin-inline-start: 0.5em;
+    }
+  }
+
+  @media (hover: hover) {
+    :host(:hover:not([disabled])) .tab {
+      color: currentColor;
+    }
+  }
+
+  :host(:focus) {
+    outline: transparent;
+  }
+
+  :host(:focus-visible) .tab {
+    outline: var(--wa-focus-ring);
+    outline-offset: calc(-1 * var(--wa-border-width-l) - var(--wa-focus-ring-offset));
+  }
+
+  :host([active]:not([disabled])) {
+    color: var(--wa-color-brand-on-quiet);
+  }
+
+  :host([disabled]) .tab {
+    opacity: 0.5;
+    cursor: not-allowed;
+  }
+
+  @media (forced-colors: active) {
+    :host([active]:not([disabled])) {
+      outline: solid 1px transparent;
+      outline-offset: -3px;
+    }
+  }
+`;var Ha=0,J=class extends P{constructor(){super(...arguments),this.attrId=++Ha,this.componentId=`wa-tab-${this.attrId}`,this.panel="",this.active=!1,this.disabled=!1,this.tabIndex=0,this.slot="nav",this.role="tab"}handleActiveChange(){this.setAttribute("aria-selected",this.active?"true":"false")}handleDisabledChange(){this.setAttribute("aria-disabled",this.disabled?"true":"false"),this.disabled&&!this.active?this.tabIndex=-1:this.tabIndex=0}render(){return this.id=this.id?.length>0?this [...]
+      <div
+        part="base tab"
+        class=${M({tab:!0,"tab-active":this.active})}
+      >
+        <slot></slot>
+      </div>
+    `}};J.css=zr;s([z(".tab")],J.prototype,"tab",2);s([l({reflect:!0})],J.prototype,"panel",2);s([l({type:Boolean,reflect:!0})],J.prototype,"active",2);s([l({type:Boolean,reflect:!0})],J.prototype,"disabled",2);s([l({type:Number,reflect:!0})],J.prototype,"tabIndex",2);s([l({reflect:!0})],J.prototype,"slot",2);s([l({reflect:!0})],J.prototype,"role",2);s([S("active")],J.prototype,"handleActiveChange",1);s([S("disabled")],J.prototype,"handleDisabledChange",1);J=s([$("wa-tab")],J);var $r=cla [...]
+  :host {
+    --indicator-color: var(--wa-color-brand-fill-loud);
+    --track-color: var(--wa-color-neutral-fill-normal);
+    --track-width: 0.125rem;
+
+    /* Private */
+    --safe-track-width: max(0.5px, round(var(--track-width), 0.5px));
+
+    display: block;
+  }
+
+  .tab-group {
+    display: flex;
+    border-radius: 0;
+  }
+
+  .tabs {
+    display: flex;
+    position: relative;
+  }
+
+  .indicator {
+    position: absolute;
+  }
+
+  .tab-group-has-scroll-controls .nav-container {
+    position: relative;
+    padding: 0 1.5em;
+  }
+
+  .body {
+    display: block;
+  }
+
+  .scroll-button {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    width: 1.5em;
+  }
+
+  .scroll-button-start {
+    inset-inline-start: 0;
+  }
+
+  .scroll-button-end {
+    inset-inline-end: 0;
+  }
+
+  /*
+    * Top
+    */
+
+  .tab-group-top {
+    flex-direction: column;
+  }
+
+  .tab-group-top .nav-container {
+    order: 1;
+  }
+
+  .tab-group-top .nav {
+    display: flex;
+    overflow-x: auto;
+
+    /* Hide scrollbar in Firefox */
+    scrollbar-width: none;
+  }
+
+  /* Hide scrollbar in Chrome/Safari */
+  .tab-group-top .nav::-webkit-scrollbar {
+    width: 0;
+    height: 0;
+  }
+
+  .tab-group-top .tabs {
+    flex: 1 1 auto;
+    position: relative;
+    flex-direction: row;
+    border-bottom: solid var(--safe-track-width) var(--track-color);
+  }
+
+  .tab-group-top .indicator {
+    bottom: calc(-1 * var(--safe-track-width));
+    border-bottom: solid var(--safe-track-width) var(--indicator-color);
+  }
+
+  .tab-group-top .body {
+    order: 2;
+  }
+
+  .tab-group-top ::slotted(wa-tab[active]) {
+    border-block-end: solid var(--safe-track-width) var(--indicator-color);
+    margin-block-end: calc(-1 * var(--safe-track-width));
+  }
+
+  .tab-group-top .body slot::slotted(wa-tab-panel) {
+    --padding: var(--wa-space-xl) 0;
+  }
+
+  /*
+    * Bottom
+    */
+
+  .tab-group-bottom {
+    flex-direction: column;
+  }
+
+  .tab-group-bottom .nav-container {
+    order: 2;
+  }
+
+  .tab-group-bottom .nav {
+    display: flex;
+    overflow-x: auto;
+
+    /* Hide scrollbar in Firefox */
+    scrollbar-width: none;
+  }
+
+  /* Hide scrollbar in Chrome/Safari */
+  .tab-group-bottom .nav::-webkit-scrollbar {
+    width: 0;
+    height: 0;
+  }
+
+  .tab-group-bottom .tabs {
+    flex: 1 1 auto;
+    position: relative;
+    flex-direction: row;
+    border-top: solid var(--safe-track-width) var(--track-color);
+  }
+
+  .tab-group-bottom .indicator {
+    top: calc(-1 * var(--safe-track-width));
+    border-top: solid var(--safe-track-width) var(--indicator-color);
+  }
+
+  .tab-group-bottom .body {
+    order: 1;
+  }
+
+  .tab-group-bottom ::slotted(wa-tab[active]) {
+    border-block-start: solid var(--safe-track-width) var(--indicator-color);
+    margin-block-start: calc(-1 * var(--safe-track-width));
+  }
+
+  .tab-group-bottom .body slot::slotted(wa-tab-panel) {
+    --padding: var(--wa-space-xl) 0;
+  }
+
+  /*
+    * Start
+    */
+
+  .tab-group-start {
+    flex-direction: row;
+  }
+
+  .tab-group-start .nav-container {
+    order: 1;
+  }
+
+  .tab-group-start .tabs {
+    flex: 0 0 auto;
+    flex-direction: column;
+    border-inline-end: solid var(--safe-track-width) var(--track-color);
+  }
+
+  .tab-group-start .indicator {
+    inset-inline-end: calc(-1 * var(--safe-track-width));
+    border-right: solid var(--safe-track-width) var(--indicator-color);
+  }
+
+  .tab-group-start .body {
+    flex: 1 1 auto;
+    order: 2;
+  }
+
+  .tab-group-start ::slotted(wa-tab[active]) {
+    border-inline-end: solid var(--safe-track-width) var(--indicator-color);
+    margin-inline-end: calc(-1 * var(--safe-track-width));
+  }
+
+  .tab-group-start .body slot::slotted(wa-tab-panel) {
+    --padding: 0 var(--wa-space-xl);
+  }
+
+  /*
+    * End
+    */
+
+  .tab-group-end {
+    flex-direction: row;
+  }
+
+  .tab-group-end .nav-container {
+    order: 2;
+  }
+
+  .tab-group-end .tabs {
+    flex: 0 0 auto;
+    flex-direction: column;
+    border-left: solid var(--safe-track-width) var(--track-color);
+  }
+
+  .tab-group-end .indicator {
+    inset-inline-start: calc(-1 * var(--safe-track-width));
+    border-inline-start: solid var(--safe-track-width) var(--indicator-color);
+  }
+
+  .tab-group-end .body {
+    flex: 1 1 auto;
+    order: 1;
+  }
+
+  .tab-group-end ::slotted(wa-tab[active]) {
+    border-inline-start: solid var(--safe-track-width) var(--indicator-color);
+    margin-inline-start: calc(-1 * var(--safe-track-width));
+  }
+
+  .tab-group-end .body slot::slotted(wa-tab-panel) {
+    --padding: 0 var(--wa-space-xl);
+  }
+`;var Z=class extends P{constructor(){super(...arguments),this.tabs=[],this.focusableTabs=[],this.panels=[],this.localize=new I(this),this.hasScrollControls=!1,this.active="",this.placement="top",this.activation="auto",this.withoutScrollControls=!1}connectedCallback(){super.connectedCallback(),!!1&&(this.resizeObserver=new ResizeObserver(()=>{this.updateScrollControls()}),this.mutationObserver=new MutationObserver(t=>{t.some(o=>!["aria-labelledby","aria-controls"].includes(o.attributeNam [...]
+      <div
+        part="base tab-group"
+        class=${M({"tab-group":!0,"tab-group-top":this.placement==="top","tab-group-bottom":this.placement==="bottom","tab-group-start":this.placement==="start","tab-group-end":this.placement==="end","tab-group-has-scroll-controls":this.hasScrollControls})}
+        @click=${this.handleClick}
+        @keydown=${this.handleKeyDown}
+      >
+        <div class="nav-container" part="nav">
+          ${this.hasScrollControls?g`
+                <wa-button
+                  part="scroll-button scroll-button-start"
+                  exportparts="base:scroll-button__base"
+                  class="scroll-button scroll-button-start"
+                  appearance="plain"
+                  @click=${this.handleScrollToStart}
+                >
+                  <wa-icon
+                    name=${t?"chevron-right":"chevron-left"}
+                    library="system"
+                    variant="solid"
+                    label=${this.localize.term("scrollToStart")}
+                  ></wa-icon>
+                </wa-button>
+              `:""}
+
+          <!-- We have a focus listener because in Firefox (and soon to be Chrome) overflow containers are focusable. -->
+          <div class="nav" @focus=${()=>this.activeTab?.focus({preventScroll:!0})}>
+            <div part="tabs" class="tabs" role="tablist">
+              <slot name="nav" @slotchange=${this.syncTabsAndPanels}></slot>
+            </div>
+          </div>
+
+          ${this.hasScrollControls?g`
+                <wa-button
+                  part="scroll-button scroll-button-end"
+                  class="scroll-button scroll-button-end"
+                  exportparts="base:scroll-button__base"
+                  appearance="plain"
+                  @click=${this.handleScrollToEnd}
+                >
+                  <wa-icon
+                    name=${t?"chevron-left":"chevron-right"}
+                    library="system"
+                    variant="solid"
+                    label=${this.localize.term("scrollToEnd")}
+                  ></wa-icon>
+                </wa-button>
+              `:""}
+        </div>
+
+        <div part="body" class="body"><slot @slotchange=${this.syncTabsAndPanels}></slot></div>
+      </div>
+    `}};Z.css=Or;s([z(".tab-group")],Z.prototype,"tabGroup",2);s([z(".body slot")],Z.prototype,"defaultSlot",2);s([z(".nav")],Z.prototype,"nav",2);s([B()],Z.prototype,"hasScrollControls",2);s([l({reflect:!0})],Z.prototype,"active",2);s([l()],Z.prototype,"placement",2);s([l()],Z.prototype,"activation",2);s([l({attribute:"without-scroll-controls",type:Boolean})],Z.prototype,"withoutScrollControls",2);s([S("active")],Z.prototype,"updateActiveTab",1);s([S("withoutScrollControls",{waitUntilFi [...]
+  :host {
+    --padding: 0;
+
+    display: none;
+  }
+
+  :host([active]) {
+    display: block;
+  }
+
+  .tab-panel {
+    display: block;
+    padding: var(--padding);
+  }
+`;var Wa=0,$t=class extends P{constructor(){super(...arguments),this.attrId=++Wa,this.componentId=`wa-tab-panel-${this.attrId}`,this.name="",this.active=!1,this.role="tabpanel"}connectedCallback(){super.connectedCallback(),this.id=(this.id||"").length>0?this.id:this.componentId}handleActiveChange(){this.setAttribute("aria-hidden",this.active?"false":"true")}render(){return g`
+      <slot
+        part="base"
+        class=${M({"tab-panel":!0,"tab-panel-active":this.active})}
+      ></slot>
+    `}};$t.css=Tr;s([l({reflect:!0})],$t.prototype,"name",2);s([l({type:Boolean,reflect:!0})],$t.prototype,"active",2);s([l({reflect:!0})],$t.prototype,"role",2);s([S("active")],$t.prototype,"handleActiveChange",1);$t=s([$("wa-tab-panel")],$t);var Pr=w`
+  :host {
+    border-width: 0;
+  }
+
+  .textarea {
+    display: grid;
+    align-items: center;
+    margin: 0;
+    border: none;
+    outline: none;
+    cursor: inherit;
+    font: inherit;
+    background-color: var(--wa-form-control-background-color);
+    border-color: var(--wa-form-control-border-color);
+    border-radius: var(--wa-form-control-border-radius);
+    border-style: var(--wa-form-control-border-style);
+    border-width: var(--wa-form-control-border-width);
+    -webkit-appearance: none;
+    outline: var(--wa-focus-ring-style) var(--wa-focus-ring-width) transparent;
+    outline-offset: var(--wa-focus-ring-offset);
+
+    &:focus-within {
+      outline-color: var(--wa-color-focus);
+    }
+
+    /* Style disabled textareas */
+    &:has(:disabled) {
+      cursor: not-allowed;
+      opacity: 0.5;
+    }
+  }
+
+  /* Appearance modifiers */
+  :host([appearance='outlined']) .textarea {
+    background-color: var(--wa-form-control-background-color);
+    border-color: var(--wa-form-control-border-color);
+  }
+
+  :host([appearance='filled']) .textarea {
+    background-color: var(--wa-color-neutral-fill-quiet);
+    border-color: var(--wa-color-neutral-fill-quiet);
+  }
+
+  :host([appearance='filled-outlined']) .textarea {
+    background-color: var(--wa-color-neutral-fill-quiet);
+    border-color: var(--wa-form-control-border-color);
+  }
+
+  textarea {
+    display: block;
+    width: 100%;
+    border: none;
+    background: transparent;
+    font: inherit;
+    color: inherit;
+    cursor: inherit;
+    scroll-padding-block: var(--wa-form-control-padding-block);
+    padding: calc(var(--wa-form-control-padding-block) - ((1lh - 1em) / 2)) var(--wa-form-control-padding-inline); /* accounts for the larger line height of textarea content */
+    min-height: calc(var(--wa-form-control-height) - var(--border-width) * 2);
+    box-shadow: none;
+    margin: 0;
+
+    &::placeholder {
+      color: var(--wa-form-control-placeholder-color);
+      user-select: none;
+      -webkit-user-select: none;
+    }
+
+    &:autofill {
+      &,
+      &:hover,
+      &:focus,
+      &:active {
+        box-shadow: none;
+        caret-color: var(--wa-form-control-value-color);
+      }
+    }
+
+    &:focus {
+      outline: none;
+    }
+  }
+
+  /* Shared textarea and size-adjuster positioning */
+  .control,
+  .size-adjuster {
+    grid-area: 1 / 1 / 2 / 2;
+  }
+
+  .size-adjuster {
+    visibility: hidden;
+    pointer-events: none;
+    opacity: 0;
+    padding: 0;
+  }
+
+  textarea::-webkit-search-decoration,
+  textarea::-webkit-search-cancel-button,
+  textarea::-webkit-search-results-button,
+  textarea::-webkit-search-results-decoration {
+    -webkit-appearance: none;
+  }
+
+  /*
+   * Resize types
+   */
+
+  :host([resize='none']) textarea {
+    resize: none;
+  }
+
+  textarea,
+  :host([resize='vertical']) textarea {
+    resize: vertical;
+  }
+
+  :host([resize='horizontal']) textarea {
+    resize: horizontal;
+  }
+
+  :host([resize='both']) textarea {
+    resize: both;
+  }
+
+  :host([resize='auto']) textarea {
+    height: auto;
+    resize: none;
+    overflow-y: hidden;
+  }
+
+  /*
+   * Footer (hint + character count)
+   */
+
+  .footer {
+    display: flex;
+    align-items: baseline;
+    gap: 1em;
+  }
+
+  .footer.has-count [part='hint'] {
+    flex: 1 1 auto;
+    min-width: 0;
+  }
+
+  .count {
+    flex: 0 0 auto;
+    color: var(--wa-form-control-hint-color);
+    font-weight: var(--wa-form-control-hint-font-weight);
+    line-height: var(--wa-form-control-hint-line-height);
+    margin-block-start: 0.5em;
+    font-size: var(--wa-font-size-smaller);
+    margin-inline-start: auto;
+  }
+`;var Rr=w`
+  .wa-visually-hidden:not(:focus-within),
+  .wa-visually-hidden-force,
+  .wa-visually-hidden-hint::part(hint),
+  .wa-visually-hidden-label::part(label),
+  .wa-visually-hidden-label::part(form-control-label) {
+    position: absolute !important;
+    width: 1px !important;
+    height: 1px !important;
+    clip: rect(0 0 0 0) !important;
+    clip-path: inset(50%) !important;
+    border: none !important;
+    overflow: hidden !important;
+    white-space: nowrap !important;
+    padding: 0 !important;
+  }
+`;var C=class extends V{constructor(){super(...arguments),this.assumeInteractionOn=["blur","input"],this.hasSlotController=new tt(this,"hint","label"),this.localize=new I(this),this.announcedCountText="",this.title="",this.name=null,this._value=null,this.defaultValue=this.getAttribute("value")??"",this.size="m",this.appearance="outlined",this.label="",this.hint="",this.placeholder="",this.rows=4,this.resize="vertical",this.disabled=!1,this.readonly=!1,this.required=!1,this.spellcheck=!0, [...]
+      <label
+        part="form-control-label label"
+        class=${M({label:!0,"has-label":o})}
+        for="input"
+        aria-hidden=${o?"false":"true"}
+      >
+        <slot name="label">${this.label}</slot>
+      </label>
+
+      <div part="base textarea-wrapper" class="textarea">
+        <textarea
+          part="textarea"
+          id="input"
+          class="control"
+          title=${this.title}
+          name=${k(this.name)}
+          .value=${no(this.value)}
+          ?disabled=${this.disabled}
+          ?readonly=${this.readonly}
+          ?required=${this.required}
+          placeholder=${k(this.placeholder)}
+          rows=${k(this.rows)}
+          minlength=${k(this.minlength)}
+          maxlength=${k(this.maxlength)}
+          autocapitalize=${k(this.autocapitalize)}
+          autocorrect=${k(this.autocorrect)}
+          ?autofocus=${this.autofocus}
+          spellcheck=${k(this.spellcheck)}
+          enterkeyhint=${k(this.enterkeyhint)}
+          inputmode=${k(this.inputmode)}
+          aria-describedby="hint"
+          @change=${this.handleChange}
+          @input=${this.handleInput}
+          @blur=${this.handleBlur}
+        ></textarea>
+
+        <!-- This "adjuster" exists to prevent layout shifting. https://github.com/shoelace-style/shoelace/issues/2180 -->
+        <div part="textarea-adjuster" class="size-adjuster" ?hidden=${this.resize!=="auto"}></div>
+      </div>
+
+      <div
+        class=${M({footer:!0,"has-count":this.withCount})}
+      >
+        <slot
+          id="hint"
+          name="hint"
+          part="hint"
+          aria-hidden=${i?"false":"true"}
+          class=${M({"has-slotted":i})}
+          >${this.hint}</slot
+        >
+
+        ${this.withCount?g`
+              <div part="count" class="count" aria-hidden="true">${a}</div>
+              <div class="wa-visually-hidden-force" aria-live="polite">${this.announcedCountText}</div>
+            `:""}
+      </div>
+    `}};C.css=[Pr,ne,et,Rr];s([B()],C.prototype,"announcedCountText",2);s([z(".control")],C.prototype,"input",2);s([z('[part~="base"]')],C.prototype,"base",2);s([z(".size-adjuster")],C.prototype,"sizeAdjuster",2);s([l()],C.prototype,"title",2);s([l({reflect:!0})],C.prototype,"name",2);s([B()],C.prototype,"value",1);s([l({attribute:"value",reflect:!0})],C.prototype,"defaultValue",2);s([l({reflect:!0})],C.prototype,"size",2);s([S("size")],C.prototype,"handleSizeChange",1);s([l({reflect:!0} [...]
+/*! Bundled license information:
+
+@awesome.me/webawesome/dist/chunks/chunk.R7QX4M6R.js:
+@awesome.me/webawesome/dist/chunks/chunk.VC3BPUZJ.js:
+@awesome.me/webawesome/dist/chunks/chunk.7VGCIHDG.js:
+@awesome.me/webawesome/dist/chunks/chunk.AOKMSJXD.js:
+@awesome.me/webawesome/dist/chunks/chunk.KBXNFZQL.js:
+@awesome.me/webawesome/dist/chunks/chunk.RWNXKUCF.js:
+@awesome.me/webawesome/dist/chunks/chunk.3CFUTVFX.js:
+@awesome.me/webawesome/dist/chunks/chunk.RPQJAXXR.js:
+@awesome.me/webawesome/dist/chunks/chunk.G5ZZIGWB.js:
+@awesome.me/webawesome/dist/chunks/chunk.XNTP7DEQ.js:
+@awesome.me/webawesome/dist/chunks/chunk.PZAN6FPN.js:
+@awesome.me/webawesome/dist/chunks/chunk.KQHZRDPB.js:
+@awesome.me/webawesome/dist/chunks/chunk.56IHH3HP.js:
+@awesome.me/webawesome/dist/chunks/chunk.AFPI375Q.js:
+@awesome.me/webawesome/dist/chunks/chunk.W7A2VLCT.js:
+@awesome.me/webawesome/dist/chunks/chunk.DVA7QY5T.js:
+@awesome.me/webawesome/dist/chunks/chunk.YDQCS2HK.js:
+@awesome.me/webawesome/dist/chunks/chunk.WDIIGUNP.js:
+@awesome.me/webawesome/dist/chunks/chunk.O74G5RVH.js:
+@awesome.me/webawesome/dist/chunks/chunk.HGBRCPUS.js:
+@awesome.me/webawesome/dist/chunks/chunk.KKI7M5DP.js:
+@awesome.me/webawesome/dist/chunks/chunk.LDM2MW63.js:
+@awesome.me/webawesome/dist/chunks/chunk.W7QZX2CB.js:
+@awesome.me/webawesome/dist/chunks/chunk.ZCZ2WKQR.js:
+@awesome.me/webawesome/dist/components/button/button.js:
+@awesome.me/webawesome/dist/chunks/chunk.HKHINDC2.js:
+@awesome.me/webawesome/dist/chunks/chunk.VQZ46MYI.js:
+@awesome.me/webawesome/dist/chunks/chunk.RMZ7BVDM.js:
+@awesome.me/webawesome/dist/chunks/chunk.4ZAKP7NY.js:
+@awesome.me/webawesome/dist/chunks/chunk.MQODJ75V.js:
+@awesome.me/webawesome/dist/chunks/chunk.PX3HMKF7.js:
+@awesome.me/webawesome/dist/chunks/chunk.3NKIHICW.js:
+@awesome.me/webawesome/dist/chunks/chunk.XTG2LNFG.js:
+@awesome.me/webawesome/dist/chunks/chunk.52WA2DJO.js:
+@awesome.me/webawesome/dist/chunks/chunk.L6CIKOFQ.js:
+@awesome.me/webawesome/dist/chunks/chunk.TP2XMZK6.js:
+@awesome.me/webawesome/dist/components/dialog/dialog.js:
+@awesome.me/webawesome/dist/chunks/chunk.2LXKNNNE.js:
+@awesome.me/webawesome/dist/chunks/chunk.4LIBWWJL.js:
+@awesome.me/webawesome/dist/chunks/chunk.Z6IK7DP4.js:
+@awesome.me/webawesome/dist/chunks/chunk.KNJT7KBU.js:
+@awesome.me/webawesome/dist/chunks/chunk.OLYEKNQ3.js:
+@awesome.me/webawesome/dist/chunks/chunk.VCKA3KNZ.js:
+@awesome.me/webawesome/dist/chunks/chunk.CS7NVNXE.js:
+@awesome.me/webawesome/dist/chunks/chunk.ZWQCGLB5.js:
+@awesome.me/webawesome/dist/chunks/chunk.HS5AYC6E.js:
+@awesome.me/webawesome/dist/chunks/chunk.64OG2H45.js:
+@awesome.me/webawesome/dist/components/dropdown/dropdown.js:
+@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js:
+@awesome.me/webawesome/dist/chunks/chunk.JTOY5KP3.js:
+@awesome.me/webawesome/dist/chunks/chunk.NUVDWQN5.js:
+@awesome.me/webawesome/dist/chunks/chunk.I2BZQ4AT.js:
+@awesome.me/webawesome/dist/chunks/chunk.5LXXXELE.js:
+@awesome.me/webawesome/dist/chunks/chunk.A65YRABB.js:
+@awesome.me/webawesome/dist/components/input/input.js:
+@awesome.me/webawesome/dist/chunks/chunk.C3KOHXUM.js:
+@awesome.me/webawesome/dist/chunks/chunk.B632VLM3.js:
+@awesome.me/webawesome/dist/components/option/option.js:
+@awesome.me/webawesome/dist/chunks/chunk.ZCRHF4FU.js:
+@awesome.me/webawesome/dist/chunks/chunk.GWSUX3V5.js:
+@awesome.me/webawesome/dist/chunks/chunk.F25QOBDY.js:
+@awesome.me/webawesome/dist/chunks/chunk.HCE4CV72.js:
+@awesome.me/webawesome/dist/chunks/chunk.HPULLNVR.js:
+@awesome.me/webawesome/dist/chunks/chunk.4AHPL3WP.js:
+@awesome.me/webawesome/dist/chunks/chunk.37OOIOGE.js:
+@awesome.me/webawesome/dist/components/select/select.js:
+@awesome.me/webawesome/dist/components/spinner/spinner.js:
+@awesome.me/webawesome/dist/chunks/chunk.R2GHHEHL.js:
+@awesome.me/webawesome/dist/chunks/chunk.4FOSDR4V.js:
+@awesome.me/webawesome/dist/components/tab/tab.js:
+@awesome.me/webawesome/dist/chunks/chunk.YBFCQDTA.js:
+@awesome.me/webawesome/dist/chunks/chunk.SKLR37OM.js:
+@awesome.me/webawesome/dist/chunks/chunk.NMA53WZH.js:
+@awesome.me/webawesome/dist/chunks/chunk.UUZ6T3PP.js:
+@awesome.me/webawesome/dist/chunks/chunk.WRIHAZWX.js:
+@awesome.me/webawesome/dist/chunks/chunk.KQ3Z6T2I.js:
+@awesome.me/webawesome/dist/components/tab-group/tab-group.js:
+@awesome.me/webawesome/dist/components/tab-panel/tab-panel.js:
+@awesome.me/webawesome/dist/chunks/chunk.WRLWYRIB.js:
+@awesome.me/webawesome/dist/chunks/chunk.2ZAJEMB4.js:
+@awesome.me/webawesome/dist/chunks/chunk.JVZTFH2D.js:
+@awesome.me/webawesome/dist/components/textarea/textarea.js:
+  (*! Copyright 2026 Fonticons, Inc. - https://webawesome.com/license *)
+
+@lit/reactive-element/css-tag.js:
+  (**
+   * @license
+   * Copyright 2019 Google LLC
+   * SPDX-License-Identifier: BSD-3-Clause
+   *)
+
+@lit/reactive-element/reactive-element.js:
+lit-html/lit-html.js:
+lit-element/lit-element.js:
+@lit/reactive-element/decorators/custom-element.js:
+@lit/reactive-element/decorators/property.js:
+@lit/reactive-element/decorators/state.js:
+@lit/reactive-element/decorators/event-options.js:
+@lit/reactive-element/decorators/base.js:
+@lit/reactive-element/decorators/query.js:
+@lit/reactive-element/decorators/query-all.js:
+@lit/reactive-element/decorators/query-async.js:
+@lit/reactive-element/decorators/query-assigned-nodes.js:
+lit-html/directive.js:
+lit-html/directives/unsafe-html.js:
+  (**
+   * @license
+   * Copyright 2017 Google LLC
+   * SPDX-License-Identifier: BSD-3-Clause
+   *)
+
+lit-html/is-server.js:
+  (**
+   * @license
+   * Copyright 2022 Google LLC
+   * SPDX-License-Identifier: BSD-3-Clause
+   *)
+
+@lit/reactive-element/decorators/query-assigned-elements.js:
+  (**
+   * @license
+   * Copyright 2021 Google LLC
+   * SPDX-License-Identifier: BSD-3-Clause
+   *)
+
+lit-html/directives/class-map.js:
+lit-html/directives/if-defined.js:
+  (**
+   * @license
+   * Copyright 2018 Google LLC
+   * SPDX-License-Identifier: BSD-3-Clause
+   *)
+
+lit-html/static.js:
+lit-html/directive-helpers.js:
+lit-html/directives/live.js:
+  (**
+   * @license
+   * Copyright 2020 Google LLC
+   * SPDX-License-Identifier: BSD-3-Clause
+   *)
+*/
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/harness.css b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/harness.css
new file mode 100644
index 00000000000..472365c7329
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/harness.css
@@ -0,0 +1,65 @@
+* { box-sizing: border-box; }
+html { color-scheme: light; font-family: system-ui, sans-serif; }
+html[data-theme="dark"] { color-scheme: dark; }
+html[data-motion="reduce"] *,
+html[data-motion="reduce"] *::before,
+html[data-motion="reduce"] *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: .001ms !important; }
+body { margin: 0; min-width: 20rem; }
+button, input, select, textarea { font: inherit; }
+button, [role="button"], a, input, select, textarea, [tabindex] { scroll-margin: 1rem; }
+[hidden] { display: none !important; }
+causeway-menubars, causeway-object, causeway-object-header, causeway-property, causeway-collection, causeway-action, causeway-action-prompt, causeway-action-result { display: block; }
+.fixture-app { min-height: 100vh; display: grid; grid-template-rows: auto 1fr auto; }
+.fixture-header { position: relative; z-index: 10; }
+.fixture-navbar { max-width: var(--fixture-shell-width, 80rem); margin-inline: auto; display: flex; align-items: center; gap: 1rem; padding: .75rem 1rem; }
+.fixture-brand { display: inline-flex; align-items: center; gap: .55rem; color: inherit; font-weight: 700; text-decoration: none; white-space: nowrap; }
+.fixture-mark { inline-size: 2rem; block-size: 2rem; display: inline-grid; place-items: center; border-radius: 50%; font-weight: 800; }
+.fixture-nav-toggle { display: none; margin-inline-start: auto; }
+.fixture-nav { display: flex; align-items: center; gap: .5rem; flex: 1; }
+.fixture-menus { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
+.fixture-menu { position: relative; }
+.fixture-menu-panel { position: absolute; inset-block-start: calc(100% + .25rem); inset-inline-start: 0; min-inline-size: 17rem; z-index: 20; padding: .5rem; }
+.fixture-menu-panel [data-menu-action], .fixture-menu-panel wa-dropdown-item { inline-size: 100%; }
+.fixture-shell-result { margin: 1rem auto 0; max-width: var(--fixture-content-width, 74rem); }
+.fixture-main { inline-size: min(calc(100% - 2rem), var(--fixture-content-width, 74rem)); margin: 1.5rem auto; }
+.fixture-heading-row { display: flex; gap: 1rem; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; }
+.fixture-page-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr); gap: 1rem; align-items: start; }
+.fixture-card { inline-size: 100%; max-inline-size: 100%; min-width: 0; padding: 1rem; }
+.fixture-card > :first-child { margin-block-start: 0; }
+.fixture-card > :last-child { margin-block-end: 0; }
+.fixture-eyebrow { font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
+.fixture-actions { display: flex; gap: .5rem; flex-wrap: wrap; }
+.fixture-status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: .75rem; }
+.fixture-status { padding: .8rem; min-height: 5rem; }
+.fixture-tabs [role="tablist"] { display: flex; gap: .25rem; flex-wrap: wrap; }
+.fixture-tabs [role="tabpanel"] { padding-block: 1rem; }
+.fixture-properties { display: grid; grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr); gap: .75rem 1rem; align-items: start; }
+.fixture-property-label { font-weight: 650; }
+.fixture-field { display: grid; gap: .3rem; }
+.fixture-field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; }
+.fixture-error { font-size: .875rem; }
+.fixture-table-region { contain: inline-size; inline-size: 100%; max-inline-size: 100%; min-inline-size: 0; overflow-x: auto; }
+.fixture-table { border-collapse: collapse; inline-size: 100%; min-inline-size: 46rem; }
+.fixture-table th, .fixture-table td { padding: .65rem .75rem; text-align: start; }
+.fixture-footer { padding: 1rem; text-align: center; }
+.fixture-dialog-form { display: grid; gap: 1rem; min-inline-size: min(32rem, calc(100vw - 4rem)); }
+.fixture-dialog-actions { display: flex; justify-content: flex-end; gap: .5rem; flex-wrap: wrap; }
+.fixture-long-value { overflow-wrap: anywhere; }
+[data-prototype-host] { min-width: 0; }
+.prototype-badge { position: fixed; inset-block-end: .75rem; inset-inline-end: .75rem; z-index: 100; padding: .35rem .65rem; border-radius: 999px; font: 600 .75rem/1 system-ui, sans-serif; background: Canvas; color: CanvasText; border: 1px solid currentColor; opacity: .85; }
+@media (max-width: 48rem) {
+  .fixture-navbar { align-items: stretch; flex-wrap: wrap; }
+  .fixture-nav-toggle { display: inline-flex; }
+  .fixture-nav { flex-basis: 100%; display: grid; }
+  .fixture-nav[data-collapsed="true"] { display: none; }
+  .fixture-menus { display: grid; align-items: stretch; }
+  .fixture-menu-panel { position: static; margin-block-start: .25rem; }
+  .fixture-page-grid { grid-template-columns: 1fr; }
+  .fixture-properties { grid-template-columns: 1fr; gap: .25rem; }
+  .fixture-property-label:not(:first-child) { margin-block-start: .75rem; }
+  .fixture-field-row { grid-template-columns: 1fr; }
+  .fixture-main { inline-size: min(calc(100% - 1rem), var(--fixture-content-width, 74rem)); margin-block: .75rem; }
+}
+@media (forced-colors: active) {
+  .fixture-card, .fixture-status, .fixture-menu-panel { border: 1px solid CanvasText; }
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/index.html b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/index.html
new file mode 100644
index 00000000000..17dedf94aeb
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/index.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <title>Causeway theming strategy analysis</title>
+  <link rel="stylesheet" href="./harness.css">
+  <style>
+    body { padding: 2rem; }
+    main { max-width: 72rem; margin-inline: auto; }
+    .candidate-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; }
+    .candidate-grid a { display: block; border: 1px solid #8c9199; border-radius: .5rem; padding: 1rem; color: inherit; text-decoration: none; }
+    .candidate-grid a:hover, .candidate-grid a:focus-visible { border-color: #075985; outline: .2rem solid #38bdf8; outline-offset: .15rem; }
+  </style>
+</head>
+<body>
+<main>
+  <p>Apache Causeway analysis harness</p>
+  <h1>Theming strategy prototypes</h1>
+  <p>Every candidate renders the same representative semantic fixture with pinned local assets.</p>
+  <div class="candidate-grid">
+    <a href="./prototype.html?candidate=baseline"><strong>Current Causeway baseline</strong><br>Existing Causeway theme and component styles.</a>
+    <a href="./prototype.html?candidate=bootstrap"><strong>Bootstrap 5.3</strong><br>CSS variables and selected classes with Causeway behavior.</a>
+    <a href="./prototype.html?candidate=webawesome"><strong>Web Awesome</strong><br>Web Components behind Causeway hosts.</a>
+    <a href="./prototype.html?candidate=openprops"><strong>Open Props + native</strong><br>Design tokens with Dialog and Popover APIs.</a>
+  </div>
+</main>
+</body>
+</html>
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/integrate-petclinic.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/integrate-petclinic.mjs
new file mode 100644
index 00000000000..bd72bb67440
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/integrate-petclinic.mjs
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {mkdirSync, readFileSync, writeFileSync} from 'node:fs';
+import {extname, resolve, sep} from 'node:path';
+import {fileURLToPath} from 'node:url';
+import {chromium} from 'playwright';
+
+const directory = resolve(fileURLToPath(new URL('.', import.meta.url)));
+const evidenceDirectory = resolve(directory, '..');
+const resultDirectory = resolve(evidenceDirectory, 'results');
+const screenshotDirectory = resolve(evidenceDirectory, 'screenshots');
+const harnessOrigin = process.env.HARNESS_ORIGIN ?? 'http://127.0.0.1:4173';
+const petclinicOrigin = process.env.PETCLINIC_ORIGIN ?? 'http://127.0.0.1:8080';
+const candidates = ['baseline', 'bootstrap', 'webawesome', 'openprops'];
+
+mkdirSync(resultDirectory, {recursive: true});
+mkdirSync(screenshotDirectory, {recursive: true});
+
+const response = await fetch(`${petclinicOrigin}/htmx`);
+if (!response.ok) throw new Error(`Petclinic is not available at ${petclinicOrigin}: HTTP ${response.status}`);
+
+const browser = await chromium.launch({
+  headless: true,
+  executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE || undefined
+});
+const evidence = {generatedAt: new Date().toISOString(), petclinicOrigin, harnessOrigin, candidates: {}};
+
+try {
+  for (const candidate of candidates) {
+    console.log(`Integrating ${candidate}...`);
+    const context = await browser.newContext({viewport: {width: 1440, height: 900}, colorScheme: 'light'});
+    const page = await context.newPage();
+    page.setDefaultTimeout(30_000);
+    await installAnalysisRoute(page);
+    const failures = [];
+    page.on('pageerror', error => failures.push({kind: 'page-error', message: String(error)}));
+    page.on('console', message => {
+      if (message.type() === 'error') failures.push({kind: 'console-error', message: message.text()});
+    });
+    page.on('requestfailed', request => failures.push({kind: 'request-failed', message: `${request.url()} ${request.failure()?.errorText ?? ''}`}));
+    await page.goto(`${petclinicOrigin}/htmx`, {waitUntil: 'networkidle'});
+    await page.waitForFunction(() => ['ready', 'partial-error'].includes(document.getElementsByTagName('causeway-menubars')[0]?.dataset.menuState));
+    await page.waitForFunction(() => ['ready', 'partial-error'].includes(document.querySelector('[data-testid="causeway-route-page"]')?.dataset.routeState));
+    const before = await snapshot(page);
+    await injectCandidate(page, candidate);
+    await page.waitForTimeout(candidate === 'webawesome' ? 800 : 250);
+    const menuTrigger = page.locator('[data-causeway-menu-disclosure]').filter({hasText: 'Pet Owners'}).first();
+    await menuTrigger.click();
+    await page.waitForTimeout(100);
+    const menuOpened = await menuTrigger.getAttribute('aria-expanded');
+    await page.keyboard.press('Escape');
+    await page.waitForTimeout(100);
+    const menuClosed = await menuTrigger.getAttribute('aria-expanded');
+    const after = await snapshot(page);
+    const screenshot = `screenshots/integration-${candidate}.jpg`;
+    await page.screenshot({path: resolve(evidenceDirectory, screenshot), fullPage: true, type: 'jpeg', quality: 82});
+    evidence.candidates[candidate] = {
+      mode: candidate === 'webawesome' ? 'single internal adapter plus toolkit theme loaded beside the real viewer' : candidate === 'baseline' ? 'unmodified real viewer' : 'candidate CSS injected into the real viewer without source changes',
+      before,
+      after,
+      menuOpened,
+      menuClosed,
+      screenshot,
+      failures
+    };
+    await context.close();
+    console.log(`Integrated ${candidate}.`);
+  }
+} finally {
+  await browser.close();
+}
+
+writeFileSync(resolve(resultDirectory, 'real-viewer-integration.json'), `${JSON.stringify(evidence, null, 2)}\n`);
+writeFileSync(resolve(resultDirectory, 'real-viewer-integration.md'), renderMarkdown(evidence));
+console.log(Object.entries(evidence.candidates).map(([candidate, result]) => `${candidate}: failures=${result.failures.length}, overflow=${result.after.horizontalOverflow}, menu=${result.menuOpened}->${result.menuClosed}, adapter=${result.after.analysisAdapterCount}`).join('\n'));
+
+async function installAnalysisRoute(page) {
+  const prefix = `${petclinicOrigin}/__analysis/`;
+  await page.route(`${petclinicOrigin}/__analysis/**`, async route => {
+    const relativePath = decodeURIComponent(route.request().url().slice(prefix.length));
+    const target = resolve(directory, relativePath);
+    if (target !== directory && !target.startsWith(directory + sep)) {
+      await route.fulfill({status: 403, body: 'Forbidden'});
+      return;
+    }
+    const contentTypes = {'.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.svg': 'image/svg+xml', '.woff2': 'font/woff2'};
+    try {
+      await route.fulfill({status: 200, contentType: contentTypes[extname(target)] ?? 'application/octet-stream', body: readFileSync(target)});
+    } catch {
+      await route.fulfill({status: 404, body: 'Not found'});
+    }
+  });
+}
+
+async function injectCandidate(page, candidate) {
+  const base = `${petclinicOrigin}/__analysis`;
+  if (candidate === 'bootstrap') {
+    await page.addStyleTag({url: `${base}/node_modules/bootstrap/dist/css/bootstrap.min.css`});
+    await page.addStyleTag({url: `${base}/candidates/bootstrap.css`});
+  } else if (candidate === 'openprops') {
+    for (const asset of ['open-props.min.css', 'normalize.min.css', 'buttons.min.css']) {
+      await page.addStyleTag({url: `${base}/node_modules/open-props/${asset}`});
+    }
+    await page.addStyleTag({url: `${base}/candidates/open-props.css`});
+  } else if (candidate === 'webawesome') {
+    const distribution = `${base}/node_modules/@awesome.me/webawesome/dist-cdn`;
+    await page.addStyleTag({url: `${distribution}/styles/webawesome.css`});
+    await page.addStyleTag({url: `${base}/candidates/web-awesome.css`});
+    await page.evaluate(async distributionPath => {
+      const {setBasePath} = await import(`${distributionPath}/utilities/base-path.js`);
+      setBasePath(distributionPath);
+      await import(`${distributionPath}/components/button/button.js`);
+      const host = document.createElement('causeway-action');
+      host.dataset.analysisAdapter = 'webawesome';
+      host.innerHTML = '<wa-button appearance="filled" variant="brand">Analysis adapter action</wa-button>';
+      document.querySelector('.causeway-object-actions, .causeway-heading-actions, [data-testid="causeway-route-page"]')?.prepend(host);
+      await customElements.whenDefined('wa-button');
+    }, distribution);
+  }
+}
+
+async function snapshot(page) {
+  return page.evaluate(() => {
+    const firstButton = document.querySelector('button');
+    const styles = firstButton ? getComputedStyle(firstButton) : null;
+    return {
+      url: location.href,
+      title: document.title,
+      causewayElementCount: [...document.querySelectorAll('*')].filter(element => element.localName.startsWith('causeway-')).length,
+      analysisAdapterCount: document.querySelectorAll('[data-analysis-adapter]').length,
+      horizontalOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
+      routeState: document.querySelector('[data-testid="causeway-route-page"]')?.dataset.routeState,
+      menuState: document.getElementsByTagName('causeway-menubars')[0]?.dataset.menuState,
+      bodyBackground: getComputedStyle(document.body).backgroundColor,
+      bodyColor: getComputedStyle(document.body).color,
+      firstButton: styles ? {color: styles.color, background: styles.backgroundColor, borderRadius: styles.borderRadius, font: styles.fontFamily} : null
+    };
+  });
+}
+
+function renderMarkdown(result) {
+  const lines = ['# Real Petclinic integration check', '', `Generated: ${result.generatedAt}`, '', 'The check injects candidate assets only into a disposable browser context and does not modify production files.', ''];
+  for (const [candidate, item] of Object.entries(result.candidates)) {
+    lines.push(`## ${candidate}`, '');
+    lines.push(`- Mode: ${item.mode}.`);
+    lines.push(`- Browser failures: ${item.failures.length}.`);
+    lines.push(`- Menu disclosure: ${item.menuOpened} before Escape, ${item.menuClosed} after Escape.`);
+    lines.push(`- Horizontal overflow before/after: ${item.before.horizontalOverflow}/${item.after.horizontalOverflow} pixels.`);
+    lines.push(`- Body background before/after: ${item.before.bodyBackground}/${item.after.bodyBackground}.`);
+    lines.push(`- First button before/after: ${JSON.stringify(item.before.firstButton)}/${JSON.stringify(item.after.firstButton)}.`);
+    lines.push(`- Internal analysis adapters: ${item.after.analysisAdapterCount}.`);
+    lines.push(`- Screenshot: ${item.screenshot}.`, '');
+  }
+  lines.push('## Interpretation', '');
+  lines.push('- Bootstrap and Open Props can affect the existing light DOM immediately through global CSS, which makes incremental styling easy but creates collision and regression risk.');
+  lines.push('- Web Awesome does not style existing Causeway controls automatically because its visual implementation lives behind toolkit-owned custom elements and shadow DOM.');
+  lines.push('- Web Awesome therefore requires explicit internal adapters or component renderer changes, while Bootstrap and Open Props can begin as token or stylesheet changes.');
+  lines.push('- All injected strategies preserved route readiness, menu readiness, Escape dismissal, and page-level overflow in this bounded check.', '');
+  return `${lines.join('\n')}\n`;
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/measure-assets.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/measure-assets.mjs
new file mode 100644
index 00000000000..ea9ddb6d042
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/measure-assets.mjs
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {gzipSync} from 'node:zlib';
+import {mkdirSync, readFileSync, readdirSync, writeFileSync} from 'node:fs';
+import {dirname, extname, resolve} from 'node:path';
+import {fileURLToPath} from 'node:url';
+
+const directory = fileURLToPath(new URL('.', import.meta.url));
+const repositoryRoot = resolve(directory, '../../../../..');
+
+const groups = {
+  baseline: [
+    ...collect('viewers/webcomponents/foundation/src', new Set(['.css', '.mjs'])),
+    ...collect('viewers/webcomponents/htmx/src/main/resources/META-INF/resources/causeway-htmx', new Set(['.css', '.mjs'])),
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/baseline.css'
+  ],
+  bootstrapCss: [
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/bootstrap/dist/css/bootstrap.min.css',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/bootstrap.css'
+  ],
+  bootstrapOptionalJavascript: [
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js'
+  ],
+  webAwesomeEntry: [
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/styles/webawesome.css',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/webawesome.loader.js',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css'
+  ],
+  webAwesomeSelectiveBundle: [
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.css',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.js',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css'
+  ],
+  webAwesomeCompleteDistribution: collect('openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn', new Set(['.css', '.js', '.svg', '.woff2'])),
+  openProps: [
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/open-props/open-props.min.css',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/open-props/normalize.min.css',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/open-props/buttons.min.css',
+    'openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/open-props.css'
+  ]
+};
+
+const measurements = Object.fromEntries(Object.entries(groups).map(([name, files]) => [name, measure(files)]));
+const result = {
+  generatedAt: new Date().toISOString(),
+  note: 'Groups measure candidate-owned assets. Shared harness files are excluded. Complete distributions are not equivalent to browser-loaded selective assets.',
+  measurements
+};
+console.log(JSON.stringify(result, null, 2));
+if (process.argv.includes('--write')) {
+  const target = resolve(directory, '../results/asset-sizes.json');
+  mkdirSync(dirname(target), {recursive: true});
+  writeFileSync(target, `${JSON.stringify(result, null, 2)}\n`);
+}
+
+function collect(relativeDirectory, extensions) {
+  const absoluteDirectory = resolve(repositoryRoot, relativeDirectory);
+  const results = [];
+  function walk(current) {
+    for (const entry of readdirSync(current, {withFileTypes: true})) {
+      const absolute = resolve(current, entry.name);
+      if (entry.isDirectory()) walk(absolute);
+      else if (extensions.has(extname(entry.name))) results.push(absolute.slice(repositoryRoot.length + 1));
+    }
+  }
+  walk(absoluteDirectory);
+  return results.sort();
+}
+
+function measure(files) {
+  let rawBytes = 0;
+  let gzipBytes = 0;
+  for (const file of files) {
+    const bytes = readFileSync(resolve(repositoryRoot, file));
+    rawBytes += bytes.length;
+    gzipBytes += gzipSync(bytes, {level: 9}).length;
+  }
+  return files.length <= 50
+    ? {fileCount: files.length, rawBytes, gzipBytes, files}
+    : {fileCount: files.length, rawBytes, gzipBytes, sampleFiles: files.slice(0, 20)};
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/package-lock.json b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/package-lock.json
new file mode 100644
index 00000000000..de6f3f0350b
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/package-lock.json
@@ -0,0 +1,988 @@
+{
+  "name": "causeway-theming-kit-analysis",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "causeway-theming-kit-analysis",
+      "dependencies": {
+        "@awesome.me/webawesome": "3.11.0",
+        "bootstrap": "5.3.8",
+        "open-props": "1.7.23"
+      },
+      "devDependencies": {
+        "esbuild": "0.28.2",
+        "playwright": "1.61.0"
+      }
+    },
+    "node_modules/@awesome.me/webawesome": {
+      "version": "3.11.0",
+      "resolved": "https://registry.npmjs.org/@awesome.me/webawesome/-/webawesome-3.11.0.tgz",
+      "integrity": "sha512-WM8kyP+AdAT3/zwNpc/YzN3Nu5pRD6ksREFZAct/DIn6KMnQs8lPVfiPbJrOpKMk8OaSnQX19iiwFGjoswXVXw==",
+      "license": "MIT",
+      "dependencies": {
+        "@ctrl/tinycolor": "4.1.0",
+        "@floating-ui/dom": "^1.6.13",
+        "@konnorr/qr-creator": "^1.0.1",
+        "@lit-labs/ssr": "^4.1.0",
+        "@lit-labs/ssr-client": "^1.1.8",
+        "@lit/context": "^1.1.6",
+        "@lit/react": "^1.0.8",
+        "@shoelace-style/animations": "^1.2.0",
+        "@shoelace-style/localize": "^3.2.3",
+        "composed-offset-position": "^0.0.6",
+        "lit": "^3.2.1",
+        "marked": "^11.2.0",
+        "nanoid": "^5.1.5"
+      },
+      "engines": {
+        "node": ">=14.17.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz",
+      "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+      "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+      "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+      "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+      "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+      "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+      "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+      "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+      "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+      "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+      "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+      "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+      "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+      "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+      "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+      "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+      "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+      "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+      "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+      "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+      "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+      "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+      "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+      "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+      "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+      "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+      "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@floating-ui/core": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz",
+      "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.12"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz",
+      "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/core": "^1.8.0",
+        "@floating-ui/utils": "^0.2.12"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.12",
+      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz",
+      "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
+      "license": "MIT"
+    },
+    "node_modules/@konnorr/qr-creator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@konnorr/qr-creator/-/qr-creator-1.1.0.tgz",
+      "integrity": "sha512-6Ae3AfG45MOqF9Qde+1YxIxKf/GUsRmcriV7iCb/HaR+Mk1YDFYP3RINDRMI8z8YMWW3BnSthOujX9RoOex/Kw==",
+      "license": "MIT"
+    },
+    "node_modules/@lit-labs/ssr": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-4.1.0.tgz",
+      "integrity": "sha512-m0zymVVlHB1ddJQ1lastsV8ROW3whFOiHJhVPQWd04MnGTkTlUUVLQctux1QlyD9BtLXNN6iASxv388vhgKMFg==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@lit-labs/ssr-client": "^1.1.7",
+        "@lit-labs/ssr-dom-shim": "^1.6.0",
+        "@lit/reactive-element": "^2.0.4",
+        "@parse5/tools": "^0.3.0",
+        "enhanced-resolve": "^5.10.0",
+        "lit": "^3.1.2",
+        "lit-element": "^4.0.4",
+        "lit-html": "^3.1.2",
+        "node-fetch": "^3.2.8",
+        "parse5": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=13.9.0"
+      },
+      "peerDependencies": {
+        "@types/node": ">=20.0.0 <25.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@lit-labs/ssr-client": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/@lit-labs/ssr-client/-/ssr-client-1.1.8.tgz",
+      "integrity": "sha512-PjGh81oKsoI64m3IDjTqqjhC7dr2uC/o0jrllUb5gRAyp/RlAHxapgJrjq9kWz97faCHLQ8jUlTi6tGm+8fgyA==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@lit/reactive-element": "^2.0.4",
+        "lit": "^3.1.2",
+        "lit-html": "^3.1.2"
+      }
+    },
+    "node_modules/@lit-labs/ssr-dom-shim": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz",
+      "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@lit/context": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.6.tgz",
+      "integrity": "sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@lit/reactive-element": "^1.6.2 || ^2.1.0"
+      }
+    },
+    "node_modules/@lit/react": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.8.tgz",
+      "integrity": "sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==",
+      "license": "BSD-3-Clause",
+      "peerDependencies": {
+        "@types/react": "17 || 18 || 19"
+      }
+    },
+    "node_modules/@lit/reactive-element": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz",
+      "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@lit-labs/ssr-dom-shim": "^1.5.0"
+      }
+    },
+    "node_modules/@parse5/tools": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@parse5/tools/-/tools-0.3.0.tgz",
+      "integrity": "sha512-zxRyTHkqb7WQMV8kTNBKWb1BeOFUKXBXTBWuxg9H9hfvQB3IwP6Iw2U75Ia5eyRxPNltmY7E8YAlz6zWwUnjKg==",
+      "license": "MIT",
+      "dependencies": {
+        "parse5": "^7.0.0"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.8",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+      "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+      "license": "MIT",
+      "peer": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
+    "node_modules/@shoelace-style/animations": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@shoelace-style/animations/-/animations-1.2.0.tgz",
+      "integrity": "sha512-avvo1xxkLbv2dgtabdewBbqcJfV0e0zCwFqkPMnHFGbJbBHorRFfMAHh1NG9ymmXn0jW95ibUVH03E1NYXD6Gw==",
+      "license": "MIT",
+      "funding": {
+        "type": "individual",
+        "url": "https://github.com/sponsors/claviska"
+      }
+    },
+    "node_modules/@shoelace-style/localize": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.2.3.tgz",
+      "integrity": "sha512-pWoZywizMilenLfQcpE9bjaqi7WKiQXJB42cxvmSPwYS76U776wJ7B5QQ0P28yVit1vpHuFZzq1CtfVFv32jaQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/react": {
+      "version": "19.2.18",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
+      "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "csstype": "^3.2.2"
+      }
+    },
+    "node_modules/@types/trusted-types": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+      "license": "MIT"
+    },
+    "node_modules/bootstrap": {
+      "version": "5.3.8",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
+      "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/twbs"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/bootstrap"
+        }
+      ],
+      "license": "MIT",
+      "peerDependencies": {
+        "@popperjs/core": "^2.11.8"
+      }
+    },
+    "node_modules/composed-offset-position": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.6.tgz",
+      "integrity": "sha512-Q7dLompI6lUwd7LWyIcP66r4WcS9u7AL2h8HaeipiRfCRPLMWqRx8fYsjb4OHi6UQFifO7XtNC2IlEJ1ozIFxw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@floating-ui/utils": "^0.2.5"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/data-uri-to-buffer": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+      "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.24.5",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz",
+      "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==",
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.3.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.28.2",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+      "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.28.2",
+        "@esbuild/android-arm": "0.28.2",
+        "@esbuild/android-arm64": "0.28.2",
+        "@esbuild/android-x64": "0.28.2",
+        "@esbuild/darwin-arm64": "0.28.2",
+        "@esbuild/darwin-x64": "0.28.2",
+        "@esbuild/freebsd-arm64": "0.28.2",
+        "@esbuild/freebsd-x64": "0.28.2",
+        "@esbuild/linux-arm": "0.28.2",
+        "@esbuild/linux-arm64": "0.28.2",
+        "@esbuild/linux-ia32": "0.28.2",
+        "@esbuild/linux-loong64": "0.28.2",
+        "@esbuild/linux-mips64el": "0.28.2",
+        "@esbuild/linux-ppc64": "0.28.2",
+        "@esbuild/linux-riscv64": "0.28.2",
+        "@esbuild/linux-s390x": "0.28.2",
+        "@esbuild/linux-x64": "0.28.2",
+        "@esbuild/netbsd-arm64": "0.28.2",
+        "@esbuild/netbsd-x64": "0.28.2",
+        "@esbuild/openbsd-arm64": "0.28.2",
+        "@esbuild/openbsd-x64": "0.28.2",
+        "@esbuild/openharmony-arm64": "0.28.2",
+        "@esbuild/sunos-x64": "0.28.2",
+        "@esbuild/win32-arm64": "0.28.2",
+        "@esbuild/win32-ia32": "0.28.2",
+        "@esbuild/win32-x64": "0.28.2"
+      }
+    },
+    "node_modules/fetch-blob": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+      "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "node-domexception": "^1.0.0",
+        "web-streams-polyfill": "^3.0.3"
+      },
+      "engines": {
+        "node": "^12.20 || >= 14.13"
+      }
+    },
+    "node_modules/formdata-polyfill": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+      "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+      "license": "MIT",
+      "dependencies": {
+        "fetch-blob": "^3.1.2"
+      },
+      "engines": {
+        "node": ">=12.20.0"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "license": "ISC"
+    },
+    "node_modules/lit": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz",
+      "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@lit/reactive-element": "^2.1.0",
+        "lit-element": "^4.2.0",
+        "lit-html": "^3.3.0"
+      }
+    },
+    "node_modules/lit-element": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz",
+      "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@lit-labs/ssr-dom-shim": "^1.5.0",
+        "@lit/reactive-element": "^2.1.0",
+        "lit-html": "^3.3.0"
+      }
+    },
+    "node_modules/lit-html": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz",
+      "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@types/trusted-types": "^2.0.2"
+      }
+    },
+    "node_modules/marked": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz",
+      "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==",
+      "license": "MIT",
+      "bin": {
+        "marked": "bin/marked.js"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "5.1.16",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz",
+      "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.js"
+      },
+      "engines": {
+        "node": "^18 || >=20"
+      }
+    },
+    "node_modules/node-domexception": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+      "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+      "deprecated": "Use your platform's native DOMException instead",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "github",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.5.0"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+      "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+      "license": "MIT",
+      "dependencies": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/node-fetch"
+      }
+    },
+    "node_modules/open-props": {
+      "version": "1.7.23",
+      "resolved": "https://registry.npmjs.org/open-props/-/open-props-1.7.23.tgz",
+      "integrity": "sha512-+xyrJmxV9QUBFbSwPROYhOg/FLXry+uuPr4R+B5EaE4556Oc18/8ZC/fL5A+/lbRSwNvA0Alh+C0KpyFWLmLZg==",
+      "license": "MIT"
+    },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/playwright": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz",
+      "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "playwright-core": "1.61.0"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "fsevents": "2.3.2"
+      }
+    },
+    "node_modules/playwright-core": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz",
+      "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "playwright-core": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tapable": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+      "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/web-streams-polyfill": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+      "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    }
+  }
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/package.json b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/package.json
new file mode 100644
index 00000000000..f62009e5f71
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/package.json
@@ -0,0 +1,23 @@
+{
+  "name": "causeway-theming-kit-analysis",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "serve": "node server.mjs",
+    "verify-assets": "node verify-assets.mjs",
+    "measure-assets": "node measure-assets.mjs",
+    "build-webawesome": "node build-webawesome.mjs",
+    "capture": "node capture.mjs --write",
+    "integrate-petclinic": "node integrate-petclinic.mjs",
+    "summarize-lighthouse": "node summarize-lighthouse.mjs"
+  },
+  "dependencies": {
+    "@awesome.me/webawesome": "3.11.0",
+    "bootstrap": "5.3.8",
+    "open-props": "1.7.23"
+  },
+  "devDependencies": {
+    "esbuild": "0.28.2",
+    "playwright": "1.61.0"
+  }
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html
new file mode 100644
index 00000000000..e2a87be8fe6
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="color-scheme" content="light dark">
+  <title>Causeway theming prototype</title>
+  <link rel="stylesheet" href="./harness.css">
+  <script type="module" src="./fixture.mjs"></script>
+</head>
+<body>
+  <div id="fixture-root" aria-live="polite">Loading prototype…</div>
+</body>
+</html>
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/server.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/server.mjs
new file mode 100644
index 00000000000..012e6b12ba5
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/server.mjs
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {createReadStream, existsSync, statSync} from 'node:fs';
+import {createServer} from 'node:http';
+import {extname, normalize, resolve, sep} from 'node:path';
+import {fileURLToPath} from 'node:url';
+
+const harnessDir = fileURLToPath(new URL('.', import.meta.url));
+const repositoryRoot = resolve(harnessDir, '../../../../..');
+const port = Number.parseInt(process.env.PORT ?? '4173', 10);
+const host = process.env.HOST ?? '127.0.0.1';
+
+const contentTypes = new Map([
+  ['.css', 'text/css; charset=utf-8'],
+  ['.html', 'text/html; charset=utf-8'],
+  ['.js', 'text/javascript; charset=utf-8'],
+  ['.json', 'application/json; charset=utf-8'],
+  ['.mjs', 'text/javascript; charset=utf-8'],
+  ['.svg', 'image/svg+xml'],
+  ['.woff2', 'font/woff2']
+]);
+
+function safePath(pathname) {
+  const decoded = decodeURIComponent(pathname.split('?')[0]);
+  const relative = normalize(decoded).replace(/^[/\\]+/, '');
+  const candidate = resolve(repositoryRoot, relative);
+  return candidate === repositoryRoot || candidate.startsWith(repositoryRoot + sep) ? candidate : null;
+}
+
+const server = createServer((request, response) => {
+  const requestUrl = new URL(request.url ?? '/', `http://${request.headers.host ?? `${host}:${port}`}`);
+  if (requestUrl.pathname === '/') {
+    response.writeHead(302, {location: '/openspec/changes/analyze-web-component-theming-kit/evidence/harness/index.html'});
+    response.end();
+    return;
+  }
+  let candidate;
+  try {
+    candidate = safePath(requestUrl.pathname);
+  } catch {
+    candidate = null;
+  }
+  if (!candidate || !existsSync(candidate) || !statSync(candidate).isFile()) {
+    response.writeHead(404, {'content-type': 'text/plain; charset=utf-8'});
+    response.end('Not found');
+    return;
+  }
+  response.writeHead(200, {
+    'access-control-allow-origin': '*',
+    'cache-control': 'no-store',
+    'content-type': contentTypes.get(extname(candidate)) ?? 'application/octet-stream'
+  });
+  createReadStream(candidate).pipe(response);
+});
+
+server.listen(port, host, () => {
+  console.log(`Causeway theming analysis harness: http://${host}:${port}/`);
+  console.log(`Serving repository root: ${repositoryRoot}`);
+});
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/summarize-lighthouse.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/summarize-lighthouse.mjs
new file mode 100644
index 00000000000..a088b3705ad
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/summarize-lighthouse.mjs
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {mkdirSync, readFileSync, readdirSync, writeFileSync} from 'node:fs';
+import {resolve} from 'node:path';
+import {fileURLToPath} from 'node:url';
+
+const directory = fileURLToPath(new URL('.', import.meta.url));
+const sourceDirectory = resolve(directory, '../lighthouse');
+const resultDirectory = resolve(directory, '../results');
+const reports = {};
+
+for (const reportName of readdirSync(sourceDirectory).sort()) {
+  const report = JSON.parse(readFileSync(resolve(sourceDirectory, reportName, 'report.json'), 'utf8'));
+  reports[reportName] = {
+    url: report.finalDisplayedUrl,
+    fetchTime: report.fetchTime,
+    lighthouseVersion: report.lighthouseVersion,
+    userAgent: report.userAgent,
+    categories: Object.fromEntries(Object.entries(report.categories).map(([name, category]) => [name, Math.round(category.score * 100)])),
+    failedAudits: Object.values(report.audits)
+      .filter(audit => audit.score !== null && audit.score < 1 && audit.scoreDisplayMode !== 'notApplicable')
+      .map(audit => ({
+        id: audit.id,
+        title: audit.title,
+        score: audit.score,
+        displayValue: audit.displayValue ?? null,
+        items: (audit.details?.items ?? []).map(item => ({
+          selector: item.node?.selector ?? null,
+          snippet: item.node?.snippet ?? null,
+          explanation: item.node?.explanation ?? null
+        }))
+      }))
+  };
+}
+
+const result = {generatedAt: new Date().toISOString(), reports};
+mkdirSync(resultDirectory, {recursive: true});
+writeFileSync(resolve(resultDirectory, 'lighthouse-summary.json'), `${JSON.stringify(result, null, 2)}\n`);
+writeFileSync(resolve(resultDirectory, 'lighthouse-summary.md'), renderMarkdown(result));
+console.log(`Summarized ${Object.keys(reports).length} Lighthouse reports.`);
+
+function renderMarkdown(summary) {
+  const lines = ['# Lighthouse summary', '', `Generated: ${summary.generatedAt}`, ''];
+  for (const [name, report] of Object.entries(summary.reports)) {
+    lines.push(`## ${name}`, '');
+    lines.push(`- Scores: ${Object.entries(report.categories).map(([category, score]) => `${category} ${score}`).join(', ')}.`);
+    lines.push(`- Failed audits: ${report.failedAudits.length}.`);
+    for (const audit of report.failedAudits) {
+      lines.push(`- ${audit.id}: ${audit.title}; affected items ${audit.items.length}.`);
+      for (const item of audit.items.slice(0, 12)) {
+        lines.push(`  - ${item.selector ?? 'document'}: ${(item.explanation ?? item.snippet ?? '').replaceAll('\n', ' ').slice(0, 240)}`);
+      }
+    }
+    lines.push('');
+  }
+  return `${lines.join('\n')}\n`;
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/verify-assets.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/verify-assets.mjs
new file mode 100644
index 00000000000..9baac76296e
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/verify-assets.mjs
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0.
+ */
+
+import {createHash} from 'node:crypto';
+import {mkdirSync, readFileSync, writeFileSync} from 'node:fs';
+import {dirname, resolve} from 'node:path';
+import {fileURLToPath} from 'node:url';
+
+const directory = fileURLToPath(new URL('.', import.meta.url));
+const lock = JSON.parse(readFileSync(resolve(directory, 'package-lock.json'), 'utf8'));
+const expected = Object.freeze({
+  'node_modules/bootstrap': {version: '5.3.8', license: 'MIT', licenseFile: 'LICENSE'},
+  'node_modules/@awesome.me/webawesome': {version: '3.11.0', license: 'MIT', licenseFile: 'LICENSE.md'},
+  'node_modules/open-props': {version: '1.7.23', license: 'MIT', licenseFile: 'LICENSE'},
+  'node_modules/esbuild': {version: '0.28.2', license: 'MIT', licenseFile: 'LICENSE.md'},
+  'node_modules/playwright': {version: '1.61.0', license: 'Apache-2.0', licenseFile: 'LICENSE'}
+});
+
+const packages = [];
+for (const [lockPath, expectation] of Object.entries(expected)) {
+  const entry = lock.packages?.[lockPath];
+  if (!entry) throw new Error(`Missing lock entry ${lockPath}`);
+  const packageDirectory = resolve(directory, lockPath);
+  const metadata = JSON.parse(readFileSync(resolve(packageDirectory, 'package.json'), 'utf8'));
+  if (entry.version !== expectation.version || metadata.version !== expectation.version) {
+    throw new Error(`${lockPath} version mismatch: lock=${entry.version}, package=${metadata.version}, expected=${expectation.version}`);
+  }
+  if (metadata.license !== expectation.license) {
+    throw new Error(`${lockPath} license mismatch: ${metadata.license} != ${expectation.license}`);
+  }
+  const licenseBytes = readFileSync(resolve(packageDirectory, expectation.licenseFile));
+  packages.push({
+    package: metadata.name,
+    version: metadata.version,
+    license: metadata.license,
+    resolved: entry.resolved,
+    integrity: entry.integrity,
+    directDependencies: Object.keys(metadata.dependencies ?? {}).sort(),
+    licenseFile: expectation.licenseFile,
+    licenseSha256: sha256(licenseBytes)
+  });
+}
+
+const selectedAssets = [
+  'node_modules/bootstrap/dist/css/bootstrap.min.css',
+  'node_modules/bootstrap/dist/js/bootstrap.bundle.min.js',
+  'node_modules/@awesome.me/webawesome/dist-cdn/styles/webawesome.css',
+  'node_modules/@awesome.me/webawesome/dist-cdn/webawesome.loader.js',
+  'node_modules/open-props/open-props.min.css',
+  'node_modules/open-props/normalize.min.css',
+  'node_modules/open-props/buttons.min.css'
+].map(relativePath => {
+  const bytes = readFileSync(resolve(directory, relativePath));
+  return {path: relativePath, bytes: bytes.length, sha256: sha256(bytes)};
+});
+
+const result = {
+  generatedAt: new Date().toISOString(),
+  command: 'npm ci --ignore-scripts --no-audit --no-fund && npm run verify-assets -- --write',
+  packages,
+  selectedAssets
+};
+console.log(JSON.stringify(result, null, 2));
+if (process.argv.includes('--write')) {
+  const target = resolve(directory, '../results/asset-verification.json');
+  mkdirSync(dirname(target), {recursive: true});
+  writeFileSync(target, `${JSON.stringify(result, null, 2)}\n`);
+}
+
+function sha256(bytes) {
+  return createHash('sha256').update(bytes).digest('hex');
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/harness/webawesome-entry.mjs b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/webawesome-entry.mjs
new file mode 100644
index 00000000000..ce766636800
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/harness/webawesome-entry.mjs
@@ -0,0 +1,13 @@
+/* Analysis-only selective Web Awesome entry point. */
+import '@awesome.me/webawesome/dist/components/button/button.js';
+import '@awesome.me/webawesome/dist/components/dialog/dialog.js';
+import '@awesome.me/webawesome/dist/components/dropdown/dropdown.js';
+import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js';
+import '@awesome.me/webawesome/dist/components/input/input.js';
+import '@awesome.me/webawesome/dist/components/option/option.js';
+import '@awesome.me/webawesome/dist/components/select/select.js';
+import '@awesome.me/webawesome/dist/components/spinner/spinner.js';
+import '@awesome.me/webawesome/dist/components/tab/tab.js';
+import '@awesome.me/webawesome/dist/components/tab-group/tab-group.js';
+import '@awesome.me/webawesome/dist/components/tab-panel/tab-panel.js';
+import '@awesome.me/webawesome/dist/components/textarea/textarea.js';
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/inventory.md b/openspec/changes/analyze-web-component-theming-kit/evidence/inventory.md
new file mode 100644
index 00000000000..a9d3f295841
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/inventory.md
@@ -0,0 +1,92 @@
+# Current Causeway viewer inventory
+
+## Scope
+
+This inventory records the baseline used for toolkit comparison on 2026-08-20.
+It covers `viewers/webcomponents/foundation`, `viewers/webcomponents/htmx`, and the HTMX Petclinic sample.
+
+## Asset and build model
+
+- The foundation contains 37 JavaScript modules and two CSS files under `viewers/webcomponents/foundation/src`.
+- Maven copies the complete foundation `src` directory to `META-INF/resources/causeway-webcomponents` without filtering or bundling.
+- The foundation Maven `test` phase runs Node's built-in test runner with single-test concurrency.
+- The HTMX module adds a server-rendered shell stylesheet and one ES module while depending on the foundation Maven artifact.
+- The HTMX shell loads foundation component styles, the theme, the HTMX shell stylesheet, HTMX 2.0.6 from a WebJar, and the Causeway HTMX ES module.
+- The web-component reactor currently has no `package.json`, npm lockfile, Vite, Rollup, Webpack, or other frontend bundler configuration.
+- The sample application adds five application-level Causeway variable references and one `60rem` responsive threshold.
+
+## Semantic component vocabulary
+
+The public element vocabulary contains these registered custom elements:
+
+- `<causeway-graphql-client>`
+- `<causeway-object-context>`
+- `<causeway-object>`
+- `<causeway-object-header>`
+- `<causeway-property>`
+- `<causeway-value>`
+- `<causeway-object-link>`
+- `<causeway-action>`
+- `<causeway-interaction-controller>`
+- `<causeway-collection>`
+- `<causeway-collection-column>`
+- `<causeway-menubars>`
+- `<causeway-menubar-primary>`
+- `<causeway-menubar-secondary>`
+- `<causeway-menubar-tertiary>`
+
+The component contract also names semantic prompt, result, editor, menu, loading, disabled, empty, error, and unsupported parts and publishes navigation, action, property, collection, layout, and menubar events.
+Toolkit prototypes must not replace this public vocabulary with toolkit tags or events.
+
+## Theme surface
+
+`theme.css` defines or consumes 63 `--causeway-*` variables covering typography, spacing, surfaces, text, links, borders, radii, shadows, status colors, action colors, navigation, shell dimensions, object layout, and menu presentation.
+`component-styles.css` consumes 22 structural variables, primarily for object groups, tabs, menus, menu panels, and action spacing.
+The HTMX shell stylesheet consumes 26 shared variables for shell, route, result, footer, status, and responsive presentation.
+
+The principal customization groups are:
+
+- Typography: family, base size, small and large sizes, and line height.
+- Spacing: `--causeway-space-1` through `--causeway-space-8`.
+- Surfaces: page, base, raised, subtle, navigation, and menu panel.
+- Color: text, muted, link, link hover, success, warning, danger, info, disabled, and focus.
+- Shape: small, medium, large, and pill radii plus weak and medium shadows.
+- Layout: content width, shell width, label width, control height, object gaps, and navigation height.
+- Menus: bar colors, borders, controls, focus, panel colors, panel shadow, and z-index.
+
+## Responsive and preference states
+
+- `48rem` is the shared narrow threshold for foundation object layouts, menu bars, menus, and the HTMX shell.
+- The Petclinic application adds a `60rem` breakpoint for its page-specific home presentation.
+- The default theme has explicit light and `prefers-color-scheme: dark` values.
+- The default theme and shell include `prefers-reduced-motion: reduce` handling.
+- The default theme includes `forced-colors: active` handling.
+- Component layout uses both media queries and a `48rem` object container query.
+
+## Browser and acceptance baseline
+
+No explicit minimum browser-version policy was found in the web-component documentation or specifications.
+The executable browser contract currently uses Playwright for Java 1.61.0 with Chromium as the required opt-in acceptance browser.
+The browser tests use desktop `1440 × 900` and narrow `390 × 844` viewports.
+The tests cover custom and generic routes, menus, browser history, responsive overflow, prompts, validation, cancellation, scalar and collection results, property editing, object actions, route focus, and unexpected console, HTTP, GraphQL, and resource failures.
+The vanilla sample integration test asserts that light, dark, reduced-motion, and forced-colors theme rules are packaged.
+
+## Representative Petclinic states
+
+The shared comparison fixture and real-viewer check must represent:
+
+- Stable brand header, primary and secondary menus, route loading, result region, content region, and footer.
+- Custom home and generic object pages.
+- Normal, hover, active, focus, disabled, loading, ready, partial-error, validation-error, empty, and terminal-error states.
+- Service action menu, action prompt, mandatory input error, select input, multiline input, cancellation, scalar result, collection result, object navigation, and void completion.
+- Object title, actions, associated actions, tabs, property labels and values, editable properties, collections, compact tables, long labels, and constrained-width overflow.
+- Desktop, narrow, light, dark, reduced-motion, and forced-colors presentation.
+
+## Baseline constraints
+
+- Public semantic elements and events are framework-neutral.
+- Domain state continues to flow through Causeway contexts and GraphQL contracts.
+- HTMX owns routing and fragment lifecycle rather than component data rendering.
+- Production assets are Maven-packaged and usable without a runtime CDN.
+- Application theming is expressed through documented Causeway variables and selectors.
+- Existing specifications avoid Bootstrap or Wicket markup in the public semantic layer.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/migration-roadmap.md b/openspec/changes/analyze-web-component-theming-kit/evidence/migration-roadmap.md
new file mode 100644
index 00000000000..6e220baa90e
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/migration-roadmap.md
@@ -0,0 +1,69 @@
+# Staged migration and rollback outline
+
+## Scope
+
+This roadmap applies only if a separate implementation proposal accepts the Bootstrap-derived theme pilot recommendation.
+No production migration occurs in the analysis change.
+
+## Stage 0: Policy and budgets
+
+- Define the supported browser floor for generic web-component viewers.
+- Confirm the initial 40 KB gzip theme-CSS budget and no-Bootstrap-JavaScript rule.
+- Clarify that public Causeway elements, semantic events, and `--causeway-*` variables remain compatibility contracts while generated internal classes do not.
+- Define contrast, Lighthouse, keyboard, responsive, dark, reduced-motion, forced-colors, screenshot, and Playwright gates.
+
+## Stage 1: Reproducible theme build
+
+- Add an analysis-derived pinned Bootstrap source dependency through an explicit WebJar or a repository-owned package lock.
+- Prefer selected Bootstrap Sass variables, functions, and mixins compiled into Causeway-owned selectors rather than loading complete global Bootstrap CSS.
+- Invoke the theme build from Maven so downstream applications continue using Maven only.
+- Package generated CSS and required license material in the foundation JAR.
+- Fail the build when generated assets differ from committed or expected checksums.
+
+## Stage 2: Opt-in sample pilot
+
+- Add a separately named Bootstrap-derived Causeway theme while retaining the current default theme unchanged.
+- Enable it only in the HTMX Petclinic and vanilla samples through configuration or an additional stylesheet.
+- Cover shell, menus, buttons, prompts, forms, validation, tabs, properties, collections, tables, status cards, loading, errors, and responsive navigation.
+- Map every application-facing customization through existing or deliberately added `--causeway-*` variables.
+
+## Stage 3: Evidence and compatibility review
+
+- Run the complete foundation tests and Petclinic Playwright suite.
+- Run desktop and narrow visual regression checks in light and dark modes.
+- Run reduced-motion and forced-colors checks.
+- Require Lighthouse accessibility 100 for representative fixture states or document a reviewed product-content exception.
+- Require zero page-level overflow and no hidden focus.
+- Compare generated CSS size, requests, route-ready timing, and LCP with the baseline.
+- Verify that existing applications that do not select the pilot theme are unchanged.
+
+## Stage 4: Adoption decision
+
+- Review sample feedback, accessibility evidence, maintenance effort, CSS budget, and application override compatibility.
+- Promote the pilot to an optional supported theme only if it improves evidence without public toolkit leakage.
+- Consider making it the default only in a later compatibility-reviewed change.
+- Remove the pilot if it does not beat the current theme on accessibility and maintainability after its build cost is included.
+
+## Rollback
+
+Rollback keeps the existing Causeway theme and component behavior as the permanent fallback.
+The implementation must isolate the pilot in separate generated assets and configuration so rollback consists of disabling the pilot stylesheet and removing its build inputs.
+No persisted data, GraphQL contract, route, semantic event, or application page requires migration.
+The current Playwright screenshots and acceptance tests define the rollback reference.
+
+## Compatibility policy
+
+- `<causeway-*>` tags, documented attributes and properties, semantic events, context behavior, and `--causeway-*` variables remain stable.
+- Bootstrap package names, Sass symbols, classes, data attributes, and JavaScript events are internal and unsupported.
+- Bootstrap JavaScript and Popper remain absent unless a later proposal demonstrates a distinct requirement not already served by Causeway or native behavior.
+- Applications may override Causeway variables and documented selectors without importing Bootstrap.
+- Theme build tooling remains a repository and release concern rather than an application requirement.
+
+## Initial budgets
+
+- Generated pilot CSS: at most 40 KB gzip.
+- Added initial JavaScript: 0 bytes for the theming pilot.
+- Added initial requests: at most 1 cacheable stylesheet.
+- Route-ready and LCP regression: less than 10% under a production-like throttled profile.
+- Accessibility: no critical or serious automated violations and no known keyboard or hidden-focus failure.
+- Page-level horizontal overflow: 0 pixels at documented desktop and narrow viewports.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/packaging-assessment.md b/openspec/changes/analyze-web-component-theming-kit/evidence/packaging-assessment.md
new file mode 100644
index 00000000000..e707eec3c56
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/packaging-assessment.md
@@ -0,0 +1,80 @@
+# Packaging, licensing, and supply-chain assessment
+
+## Direct packages
+
+| Package | Version | License | Production runtime package closure |
+|---|---:|---|---:|
+| `bootstrap` | 5.3.8 | MIT | CSS package alone has no direct runtime dependency; optional interactive behavior adds Popper |
+| `@awesome.me/webawesome` | 3.11.0 | MIT core package | 35 resolved transitive package instances in the analysis installation |
+| `open-props` | 1.7.23 | MIT | No reported runtime dependency |
+
+The analysis harness also pins Playwright 1.61.0 under Apache-2.0 and esbuild 0.28.2 under MIT as development-only evidence and selective-bundling tools.
+The package lock records registry URLs and integrity hashes for every resolved package.
+`npm ci --ignore-scripts` recreated the complete installation without running package lifecycle scripts.
+
+## Transitive license observation
+
+The resolved Web Awesome production closure reported 24 MIT packages, 9 BSD-3-Clause packages, 1 BSD-2-Clause package, and 1 ISC package among locally resolved metadata.
+One `@types/node` entry was nested outside the simple top-level metadata scan and requires normal automated license-report handling in a production proposal.
+No evaluated core feature depends on Web Awesome Pro or another commercial asset.
+A production dependency review must generate a complete transitive license report and update ASF notice material rather than relying on this bounded scan.
+
+## Security observation
+
+`npm audit --omit=dev --json` and `npm audit --json` both reported zero known advisories across all severities on 2026-08-20.
+This is a point-in-time registry advisory result and does not replace ASF dependency review, source provenance, release vote checks, or an ongoing update policy.
+
+## Offline Maven proof
+
+The analysis-only Maven project under `evidence/packaging` successfully built a standard JAR after pinned npm acquisition.
+The final JAR contained 26 entries and was 226,029 bytes.
+It packaged:
+
+- 2 Bootstrap browser files.
+- 2 selectively bundled Web Awesome browser files.
+- 3 Open Props CSS files.
+- 3 candidate license files.
+
+The Web Awesome bundle includes the twelve components exercised by the fixture and proves that a Maven-packaged selective build is feasible.
+A production build must reduce that set to the first adopted slice and enforce the agreed bundle budget.
+
+## Candidate paths
+
+### Bootstrap
+
+Bootstrap 5.3.8 is already resolvable in the repository's wider Maven dependency graph as a WebJar.
+The preferred production path is an explicit WebJar dependency or a selective Sass build packaged by Maven.
+No npm step is required for the complete compiled CSS path.
+The implementation must not rely on Bootstrap being present transitively through Wicket.
+
+### Web Awesome
+
+No Web Awesome artifact was found in the Maven Central WebJar search during the evaluation.
+The production path is a repository-owned `package.json` and lockfile, pinned npm acquisition invoked by Maven, selective bundling, checksum verification, and copying generated immutable assets and licenses into the foundation JAR.
+Downstream applications continue consuming Maven artifacts and do not run npm.
+The optional Web Awesome Pro distribution is outside scope.
+
+### Open Props
+
+Maven Central contains an `org.webjars.npm:open-props` artifact, but the indexed release trails 1.7.23.
+The production path can update the WebJar or use Maven-invoked pinned npm acquisition and copy only selected CSS prop packs or generated resolved tokens into the foundation JAR.
+No browser JavaScript is required.
+
+## Update policy
+
+Any adopted package requires:
+
+- An exact version and lockfile update through review.
+- Integrity and selected-asset checksum verification.
+- Automated advisory and transitive license reports.
+- Regenerated Maven assets with a clean-tree comparison.
+- Browser acceptance and visual regression execution.
+- A documented response path for abandoned or compromised upstream projects.
+
+## Hard-gate result
+
+- Bootstrap passes licensing, provenance, offline packaging, and maintenance gates.
+- Web Awesome core passes the bounded licensing and offline-packaging gates, but selective bundling and full transitive review are mandatory before adoption.
+- Open Props passes licensing and packaging gates; native Popover browser policy remains conditional.
+- Material Web fails the strategic-maintenance preference and remains comparison-only.
+- Shoelace fails the active-maintenance gate and remains rejected.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/packaging/README.md b/openspec/changes/analyze-web-component-theming-kit/evidence/packaging/README.md
new file mode 100644
index 00000000000..6cb0223ac38
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/packaging/README.md
@@ -0,0 +1,57 @@
+# Maven packaging demonstration
+
+This analysis-only Maven project proves that pinned candidate assets can be placed in a conventional JAR under `META-INF/resources` with their license files.
+It is not part of the repository reactor and must not become a production dependency.
+
+## Build
+
+First recreate the pinned npm assets:
+
+```shell
+cd ../harness
+PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm ci --ignore-scripts --no-audit --no-fund
+npm run build-webawesome
+cd ../packaging
+mvn package
+```
+
+The output JAR is `target/causeway-theming-candidate-assets-1.0.0-analysis.jar`.
+Inspect it with:
+
+```shell
+jar tf target/causeway-theming-candidate-assets-1.0.0-analysis.jar
+```
+
+The expected roots are:
+
+- `META-INF/resources/causeway-toolkit/bootstrap`
+- `META-INF/resources/causeway-toolkit/webawesome`
+- `META-INF/resources/causeway-toolkit/open-props`
+- `META-INF/licenses/bootstrap`
+- `META-INF/licenses/webawesome`
+- `META-INF/licenses/open-props`
+
+## Candidate-specific production paths
+
+### Bootstrap
+
+Bootstrap 5.3.8 is already available to the wider repository through Maven WebJar coordinates.
+A production implementation can declare an explicit WebJar dependency and serve the compiled CSS without introducing npm.
+Bootstrap JavaScript and Popper are excluded from the CSS-only prototype and would require separate dependency and behavior review.
+
+### Web Awesome
+
+No current Web Awesome WebJar was found in Maven Central during the evaluation.
+A production implementation would run pinned `npm ci` from Maven, selectively bundle required components, and package generated immutable assets and licenses into the foundation JAR.
+The analysis JAR packages the reproducible selective JavaScript and CSS bundles generated by `npm run build-webawesome` rather than the 662-file browser distribution.
+
+### Open Props
+
+Maven Central contains `org.webjars.npm:open-props`, but the indexed WebJar version trails the frozen npm release.
+A production implementation can either update the WebJar or run pinned npm acquisition and copy only the selected CSS prop packs into the foundation JAR.
+No Open Props JavaScript runtime is required.
+
+## Release boundary
+
+A production proposal must make Maven the single release entry point, verify `package-lock.json`, disable package lifecycle scripts unless reviewed, retain license and notice material, and fail when generated assets differ from their pinned checksums.
+The frontend acquisition or bundling step must be isolated from downstream applications, which continue consuming ordinary Maven artifacts.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/packaging/pom.xml b/openspec/changes/analyze-web-component-theming-kit/evidence/packaging/pom.xml
new file mode 100644
index 00000000000..9c4d143b8d5
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/packaging/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.causeway.analysis</groupId>
+    <artifactId>causeway-theming-candidate-assets</artifactId>
+    <version>1.0.0-analysis</version>
+    <packaging>jar</packaging>
+
+    <name>Apache Causeway Theming Candidate Assets - Analysis Only</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.install.skip>true</maven.install.skip>
+        <maven.deploy.skip>true</maven.deploy.skip>
+    </properties>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>../harness/node_modules/bootstrap/dist/css</directory>
+                <targetPath>META-INF/resources/causeway-toolkit/bootstrap</targetPath>
+                <filtering>false</filtering>
+                <includes>
+                    <include>bootstrap.min.css</include>
+                    <include>bootstrap.min.css.map</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>../harness/node_modules/bootstrap</directory>
+                <targetPath>META-INF/licenses/bootstrap</targetPath>
+                <filtering>false</filtering>
+                <includes>
+                    <include>LICENSE</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>../harness/generated</directory>
+                <targetPath>META-INF/resources/causeway-toolkit/webawesome</targetPath>
+                <filtering>false</filtering>
+                <includes>
+                    <include>webawesome-selective.css</include>
+                    <include>webawesome-selective.js</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>../harness/node_modules/@awesome.me/webawesome</directory>
+                <targetPath>META-INF/licenses/webawesome</targetPath>
+                <filtering>false</filtering>
+                <includes>
+                    <include>LICENSE.md</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>../harness/node_modules/open-props</directory>
+                <targetPath>META-INF/resources/causeway-toolkit/open-props</targetPath>
+                <filtering>false</filtering>
+                <includes>
+                    <include>open-props.min.css</include>
+                    <include>normalize.min.css</include>
+                    <include>buttons.min.css</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>../harness/node_modules/open-props</directory>
+                <targetPath>META-INF/licenses/open-props</targetPath>
+                <filtering>false</filtering>
+                <includes>
+                    <include>LICENSE</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/performance.md b/openspec/changes/analyze-web-component-theming-kit/evidence/performance.md
new file mode 100644
index 00000000000..63b9277e077
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/performance.md
@@ -0,0 +1,63 @@
+# Asset and performance evidence
+
+## Static asset groups
+
+The asset measurement script compresses each file independently with gzip level 9 and excludes shared harness files.
+
+| Group | Files | Raw bytes | Gzip bytes |
+|---|---:|---:|---:|
+| Current foundation and HTMX production assets plus baseline adapter | 43 | 393,535 | 107,443 |
+| Bootstrap CSS plus candidate adapter | 2 | 233,395 | 31,084 |
+| Optional Bootstrap JavaScript bundle with Popper | 1 | 80,496 | 23,812 |
+| Web Awesome entry CSS, loader, and adapter | 3 | 3,820 | 1,325 |
+| Selective Web Awesome bundle for twelve fixture components plus adapter | 3 | 378,423 | 84,848 |
+| Complete Web Awesome `dist-cdn` measurable browser asset set | 662 | 2,462,791 | 675,990 |
+| Open Props packs plus candidate adapter | 4 | 50,947 | 12,028 |
+
+The baseline group includes JavaScript modules used by the real semantic component library, while the CSS-oriented candidates do not replace that JavaScript.
+Candidate totals therefore represent additional or alternative theme payloads rather than equivalent full viewers.
+
+## Browser-loaded fixture medians
+
+Measurements use one warm-up and five local unthrottled loads with `Cache-Control: no-store`.
+Values are useful for relative request and payload comparison but are not production network benchmarks.
+
+| Strategy | Candidate requests | Encoded bytes | DOM content loaded | Load | FCP | LCP |
+|---|---:|---:|---:|---:|---:|---:|
+| Baseline fixture styles | 4 | 36,055 | 6.5 ms | 8.2 ms | 16 ms | 16 ms |
+| Bootstrap CSS | 2 | 233,395 | 5.3 ms | 8.2 ms | 20 ms | 20 ms |
+| Web Awesome browser autoloader | 132 | 615,233 | 3.8 ms | 11.2 ms | 12 ms | 32 ms |
+| Open Props packs | 4 | 50,947 | 5.8 ms | 8.2 ms | 12 ms | 16 ms |
+
+Bootstrap's source CSS is large but compresses efficiently.
+A production Bootstrap integration should compile or select only used Sass layers rather than treating the complete distribution as a budget target.
+Web Awesome's `dist-cdn` autoloader is convenient for analysis but creates 132 local requests for the representative fixture and is rejected as a production loading strategy.
+The reproducible selective bundle reduced delivery to one 70,009-byte-gzip JavaScript file and one 14,124-byte-gzip CSS file before the 715-byte-gzip adapter.
+That 84,848-byte combined payload is credible but exceeds the initial 50 KB JavaScript budget and remains substantially larger than either CSS-oriented strategy.
+Open Props has the smallest candidate payload, but Causeway must still ship its existing component behavior and a curated adapter theme.
+
+## Request interpretation
+
+- Baseline fixture requests include the foundation theme, structural component styles, HTMX shell style, and the analysis adapter.
+- Bootstrap requests include complete compiled Bootstrap CSS and the analysis adapter; Bootstrap JavaScript is excluded because Causeway behavior remains authoritative.
+- Web Awesome requests include theme imports, loader, selected components, and their shared Lit, style, localization, positioning, and utility chunks.
+- Open Props requests include the core props, normalize pack, buttons pack, and adapter.
+
+## Real-viewer integration
+
+The bounded Petclinic injection check produced zero browser failures and zero page-level overflow for every strategy.
+Bootstrap changed the first real viewer button from a 4-pixel to a 0-pixel border radius and replaced the inherited font stack, demonstrating global collision before purposeful class adoption.
+Open Props changed the generic control font and radius through its normalize and button packs.
+Web Awesome left ordinary Causeway controls largely unchanged and required one explicit internal adapter element to display a toolkit control.
+
+## Proposed production budgets
+
+A follow-on implementation should use these initial compressed budgets:
+
+- Theme CSS added to the existing viewer: no more than 40 KB gzip.
+- Toolkit JavaScript added to the existing viewer: no more than 50 KB gzip for the first adopted slice.
+- Candidate-owned initial requests: no more than 8 before HTTP caching.
+- No font or icon download unless separately justified, pinned, and packaged.
+- No regression beyond 10% in route-ready or largest-contentful-paint medians under a throttled production-like profile.
+
+These are decision budgets rather than established project policy and require review in the implementation proposal.
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/asset-sizes.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/asset-sizes.json
new file mode 100644
index 00000000000..a8f86b7b459
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/asset-sizes.json
@@ -0,0 +1,131 @@
+{
+  "generatedAt": "2026-08-20T23:17:42.149Z",
+  "note": "Groups measure candidate-owned assets. Shared harness files are excluded. Complete distributions are not equivalent to browser-loaded selective assets.",
+  "measurements": {
+    "baseline": {
+      "fileCount": 43,
+      "rawBytes": 393535,
+      "gzipBytes": 107443,
+      "files": [
+        "viewers/webcomponents/foundation/src/action-element.mjs",
+        "viewers/webcomponents/foundation/src/application-operations.mjs",
+        "viewers/webcomponents/foundation/src/collection-column-element.mjs",
+        "viewers/webcomponents/foundation/src/collection-element.mjs",
+        "viewers/webcomponents/foundation/src/component-contracts.mjs",
+        "viewers/webcomponents/foundation/src/component-styles.css",
+        "viewers/webcomponents/foundation/src/component-styles.mjs",
+        "viewers/webcomponents/foundation/src/context-consumer-element.mjs",
+        "viewers/webcomponents/foundation/src/context-events.mjs",
+        "viewers/webcomponents/foundation/src/editor-registry.mjs",
+        "viewers/webcomponents/foundation/src/graphql-client-element.mjs",
+        "viewers/webcomponents/foundation/src/graphql-client.mjs",
+        "viewers/webcomponents/foundation/src/graphql-executor.mjs",
+        "viewers/webcomponents/foundation/src/index.mjs",
+        "viewers/webcomponents/foundation/src/interaction-controller-element.mjs",
+        "viewers/webcomponents/foundation/src/interaction-operations.mjs",
+        "viewers/webcomponents/foundation/src/introspection.mjs",
+        "viewers/webcomponents/foundation/src/menu-context-controller.mjs",
+        "viewers/webcomponents/foundation/src/menu-layout.mjs",
+        "viewers/webcomponents/foundation/src/menubar-element.mjs",
+        "viewers/webcomponents/foundation/src/menubars-element.mjs",
+        "viewers/webcomponents/foundation/src/object-context-controller.mjs",
+        "viewers/webcomponents/foundation/src/object-context-element.mjs",
+        "viewers/webcomponents/foundation/src/object-element.mjs",
+        "viewers/webcomponents/foundation/src/object-header-element.mjs",
+        "viewers/webcomponents/foundation/src/object-layout.mjs",
+        "viewers/webcomponents/foundation/src/object-link-element.mjs",
+        "viewers/webcomponents/foundation/src/property-element.mjs",
+        "viewers/webcomponents/foundation/src/register.mjs",
+        "viewers/webcomponents/foundation/src/rendering.mjs",
+        "viewers/webcomponents/foundation/src/schema-names.mjs",
+        "viewers/webcomponents/foundation/src/selection.mjs",
+        "viewers/webcomponents/foundation/src/service-action-context.mjs",
+        "viewers/webcomponents/foundation/src/structural-resource.mjs",
+        "viewers/webcomponents/foundation/src/structural-xml.mjs",
+        "viewers/webcomponents/foundation/src/theme.css",
+        "viewers/webcomponents/foundation/src/types.mjs",
+        "viewers/webcomponents/foundation/src/value-element.mjs",
+        "viewers/webcomponents/foundation/src/value-renderers.mjs",
+        "viewers/webcomponents/htmx/src/main/resources/META-INF/resources/causeway-htmx/causeway-htmx.css",
+        "viewers/webcomponents/htmx/src/main/resources/META-INF/resources/causeway-htmx/causeway-htmx.mjs",
+        "viewers/webcomponents/htmx/src/main/resources/META-INF/resources/causeway-htmx/route-policy.mjs",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/baseline.css"
+      ]
+    },
+    "bootstrapCss": {
+      "fileCount": 2,
+      "rawBytes": 233395,
+      "gzipBytes": 31084,
+      "files": [
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/bootstrap/dist/css/bootstrap.min.css",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/bootstrap.css"
+      ]
+    },
+    "bootstrapOptionalJavascript": {
+      "fileCount": 1,
+      "rawBytes": 80496,
+      "gzipBytes": 23812,
+      "files": [
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
+      ]
+    },
+    "webAwesomeEntry": {
+      "fileCount": 3,
+      "rawBytes": 3820,
+      "gzipBytes": 1325,
+      "files": [
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/styles/webawesome.css",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/webawesome.loader.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css"
+      ]
+    },
+    "webAwesomeSelectiveBundle": {
+      "fileCount": 3,
+      "rawBytes": 378423,
+      "gzipBytes": 84848,
+      "files": [
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.css",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/generated/webawesome-selective.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/web-awesome.css"
+      ]
+    },
+    "webAwesomeCompleteDistribution": {
+      "fileCount": 662,
+      "rawBytes": 2462791,
+      "gzipBytes": 675990,
+      "sampleFiles": [
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.26NNVZGF.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.26QE47KB.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2BXLTQVW.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2HGX567O.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2K2WWLRP.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2LXKNNNE.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2NOGQWD2.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2NV6QW6C.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2QMPIFI4.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2SO23SX3.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2VKZLNWX.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.2YEETXBR.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3A5U2WZZ.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3FDTCWBL.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3I2YK6WG.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3KVD5FFW.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3MALJW2P.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3MSWQ3RG.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3NKIHICW.js",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/@awesome.me/webawesome/dist-cdn/chunks/chunk.3OMDB7G7.js"
+      ]
+    },
+    "openProps": {
+      "fileCount": 4,
+      "rawBytes": 50947,
+      "gzipBytes": 12028,
+      "files": [
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/open-props/open-props.min.css",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/open-props/normalize.min.css",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/node_modules/open-props/buttons.min.css",
+        "openspec/changes/analyze-web-component-theming-kit/evidence/harness/candidates/open-props.css"
+      ]
+    }
+  }
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/asset-verification.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/asset-verification.json
new file mode 100644
index 00000000000..e732876d5d2
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/asset-verification.json
@@ -0,0 +1,109 @@
+{
+  "generatedAt": "2026-08-20T23:16:07.903Z",
+  "command": "npm ci --ignore-scripts --no-audit --no-fund && npm run verify-assets -- --write",
+  "packages": [
+    {
+      "package": "bootstrap",
+      "version": "5.3.8",
+      "license": "MIT",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
+      "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
+      "directDependencies": [],
+      "licenseFile": "LICENSE",
+      "licenseSha256": "4620c84ad5ce8602ff65640ed6b7c8b78ebb9e036584f0ebc1ccc88206a4bb51"
+    },
+    {
+      "package": "@awesome.me/webawesome",
+      "version": "3.11.0",
+      "license": "MIT",
+      "resolved": "https://registry.npmjs.org/@awesome.me/webawesome/-/webawesome-3.11.0.tgz",
+      "integrity": "sha512-WM8kyP+AdAT3/zwNpc/YzN3Nu5pRD6ksREFZAct/DIn6KMnQs8lPVfiPbJrOpKMk8OaSnQX19iiwFGjoswXVXw==",
+      "directDependencies": [
+        "@ctrl/tinycolor",
+        "@floating-ui/dom",
+        "@konnorr/qr-creator",
+        "@lit-labs/ssr",
+        "@lit-labs/ssr-client",
+        "@lit/context",
+        "@lit/react",
+        "@shoelace-style/animations",
+        "@shoelace-style/localize",
+        "composed-offset-position",
+        "lit",
+        "marked",
+        "nanoid"
+      ],
+      "licenseFile": "LICENSE.md",
+      "licenseSha256": "e024db6c0a83b08f33eedda3eb92d90439169903f7d9d7cfeb200c989cd62881"
+    },
+    {
+      "package": "open-props",
+      "version": "1.7.23",
+      "license": "MIT",
+      "resolved": "https://registry.npmjs.org/open-props/-/open-props-1.7.23.tgz",
+      "integrity": "sha512-+xyrJmxV9QUBFbSwPROYhOg/FLXry+uuPr4R+B5EaE4556Oc18/8ZC/fL5A+/lbRSwNvA0Alh+C0KpyFWLmLZg==",
+      "directDependencies": [],
+      "licenseFile": "LICENSE",
+      "licenseSha256": "fed3aa342094f6bb33701d09133c159a4c124e509ede255a889dd2c28ec3ab6e"
+    },
+    {
+      "package": "esbuild",
+      "version": "0.28.2",
+      "license": "MIT",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+      "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+      "directDependencies": [],
+      "licenseFile": "LICENSE.md",
+      "licenseSha256": "b40ec5baec7bb34fa5b1c09521fa3cd52d5fad7adafed74932a2010d3612a681"
+    },
+    {
+      "package": "playwright",
+      "version": "1.61.0",
+      "license": "Apache-2.0",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz",
+      "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
+      "directDependencies": [
+        "playwright-core"
+      ],
+      "licenseFile": "LICENSE",
+      "licenseSha256": "45873d00a0dd243596deb4aa23b2493b3d1f0671921bf2538ea431d7380220eb"
+    }
+  ],
+  "selectedAssets": [
+    {
+      "path": "node_modules/bootstrap/dist/css/bootstrap.min.css",
+      "bytes": 232111,
+      "sha256": "d85327d99c7a3ee1f9b5d0500d1370acea3ad2db39c163c2f51f232baedbdede"
+    },
+    {
+      "path": "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
+      "bytes": 80496,
+      "sha256": "e4fd49181388c48ec5040bd3fe66f57c29c8e67fcd8502b3354b96ec7ab47cc7"
+    },
+    {
+      "path": "node_modules/@awesome.me/webawesome/dist-cdn/styles/webawesome.css",
+      "bytes": 195,
+      "sha256": "430b1d49fda2eb30becc5aa1713e93b28b3e818db45ae03e8b6bd45bcbc27ef7"
+    },
+    {
+      "path": "node_modules/@awesome.me/webawesome/dist-cdn/webawesome.loader.js",
+      "bytes": 1442,
+      "sha256": "1723775ca7a6e28552e21b49753cdb95cae5cf86d28787b1488bde6190b4d9f4"
+    },
+    {
+      "path": "node_modules/open-props/open-props.min.css",
+      "bytes": 29566,
+      "sha256": "4bc9bec663eb7fef2cb7680d2983e21f0f79778a61a77bee74224030dc981f5c"
+    },
+    {
+      "path": "node_modules/open-props/normalize.min.css",
+      "bytes": 9041,
+      "sha256": "4de6907a35c5f343da7b6c6683f71b8cdd990619bb78fe1152d5e472856b5b03"
+    },
+    {
+      "path": "node_modules/open-props/buttons.min.css",
+      "bytes": 9181,
+      "sha256": "0df32ae2a066d72609ad51598f987e47301144e07100c67e0dd31012cfe06631"
+    }
+  ]
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/browser-evidence.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/browser-evidence.json
new file mode 100644
index 00000000000..ae36468ba66
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/browser-evidence.json
@@ -0,0 +1,852 @@
+{
+  "generatedAt": "2026-08-20T23:16:09.223Z",
+  "origin": "http://127.0.0.1:4173",
+  "playwrightVersion": "1.61.0",
+  "candidates": {
+    "baseline": {
+      "screenshots": [
+        {
+          "case": "desktop-light-menu",
+          "file": "screenshots/baseline-desktop-light-menu.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline&theme=light&state=menu-open&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(243, 245, 248)",
+            "bodyColor": "rgb(31, 41, 55)"
+          },
+          "failures": []
+        },
+        {
+          "case": "desktop-dark-prompt",
+          "file": "screenshots/baseline-desktop-dark-prompt.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline&theme=dark&state=prompt&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(16, 23, 34)",
+            "bodyColor": "rgb(232, 237, 244)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-light-nav",
+          "file": "screenshots/baseline-narrow-light-nav.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline&theme=light&state=responsive-nav&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(243, 245, 248)",
+            "bodyColor": "rgb(31, 41, 55)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-dark-all",
+          "file": "screenshots/baseline-narrow-dark-all.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline&theme=dark&state=all&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(16, 23, 34)",
+            "bodyColor": "rgb(232, 237, 244)"
+          },
+          "failures": []
+        }
+      ],
+      "interactions": {
+        "menuOpened": {
+          "open": true,
+          "ariaExpanded": "true",
+          "panelHidden": false,
+          "panelDisplay": "block",
+          "focused": true
+        },
+        "menuEscape": {
+          "open": false,
+          "ariaExpanded": "false",
+          "panelHidden": true,
+          "panelDisplay": "none",
+          "focused": true
+        },
+        "actionSelection": {
+          "menu": {
+            "open": false,
+            "ariaExpanded": "false",
+            "panelHidden": true,
+            "panelDisplay": "none",
+            "focused": false
+          },
+          "prompt": {
+            "open": true,
+            "localName": "dialog"
+          },
+          "focused": {
+            "localName": "input",
+            "id": "owner-name",
+            "text": null,
+            "testField": true
+          }
+        },
+        "promptEscape": {
+          "prompt": {
+            "open": false,
+            "localName": "dialog"
+          },
+          "focused": {
+            "localName": "button",
+            "id": null,
+            "text": "Pet Owners",
+            "testField": false
+          }
+        },
+        "tabs": [
+          {
+            "text": "Details",
+            "active": "false",
+            "focused": false
+          },
+          {
+            "text": "Notes and preferences",
+            "active": "true",
+            "focused": true
+          }
+        ],
+        "failures": []
+      },
+      "media": {
+        "reducedMotion": true,
+        "forcedColors": true,
+        "dark": true,
+        "horizontalOverflow": 0,
+        "navVisible": true,
+        "failures": []
+      },
+      "performance": {
+        "samples": [
+          {
+            "domContentLoaded": 7.399999976158142,
+            "load": 9.300000011920929,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 20,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 37255,
+            "candidateEncodedBytes": 36055,
+            "candidateResourceDuration": 5.199999988079071
+          },
+          {
+            "domContentLoaded": 3.400000035762787,
+            "load": 4.800000011920929,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 12,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 37255,
+            "candidateEncodedBytes": 36055,
+            "candidateResourceDuration": 3.699999988079071
+          },
+          {
+            "domContentLoaded": 6.100000023841858,
+            "load": 7.900000035762787,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 37255,
+            "candidateEncodedBytes": 36055,
+            "candidateResourceDuration": 5.399999976158142
+          },
+          {
+            "domContentLoaded": 7.199999988079071,
+            "load": 9.299999952316284,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 37255,
+            "candidateEncodedBytes": 36055,
+            "candidateResourceDuration": 5.799999892711639
+          },
+          {
+            "domContentLoaded": 5.5,
+            "load": 7.600000023841858,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 37255,
+            "candidateEncodedBytes": 36055,
+            "candidateResourceDuration": 5.100000023841858
+          }
+        ],
+        "median": {
+          "domContentLoaded": 6.100000023841858,
+          "load": 7.900000035762787,
+          "firstContentfulPaint": 16,
+          "largestContentfulPaint": 16,
+          "candidateRequestCount": 4,
+          "candidateTransferBytes": 37255,
+          "candidateEncodedBytes": 36055,
+          "candidateResourceDuration": 5.199999988079071
+        }
+      }
+    },
+    "bootstrap": {
+      "screenshots": [
+        {
+          "case": "desktop-light-menu",
+          "file": "screenshots/bootstrap-desktop-light-menu.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap&theme=light&state=menu-open&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(248, 249, 250)",
+            "bodyColor": "rgb(33, 37, 41)"
+          },
+          "failures": []
+        },
+        {
+          "case": "desktop-dark-prompt",
+          "file": "screenshots/bootstrap-desktop-dark-prompt.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap&theme=dark&state=prompt&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(43, 48, 53)",
+            "bodyColor": "rgb(222, 226, 230)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-light-nav",
+          "file": "screenshots/bootstrap-narrow-light-nav.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap&theme=light&state=responsive-nav&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(248, 249, 250)",
+            "bodyColor": "rgb(33, 37, 41)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-dark-all",
+          "file": "screenshots/bootstrap-narrow-dark-all.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap&theme=dark&state=all&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(43, 48, 53)",
+            "bodyColor": "rgb(222, 226, 230)"
+          },
+          "failures": []
+        }
+      ],
+      "interactions": {
+        "menuOpened": {
+          "open": true,
+          "ariaExpanded": "true",
+          "panelHidden": false,
+          "panelDisplay": "block",
+          "focused": true
+        },
+        "menuEscape": {
+          "open": false,
+          "ariaExpanded": "false",
+          "panelHidden": true,
+          "panelDisplay": "none",
+          "focused": true
+        },
+        "actionSelection": {
+          "menu": {
+            "open": false,
+            "ariaExpanded": "false",
+            "panelHidden": true,
+            "panelDisplay": "none",
+            "focused": false
+          },
+          "prompt": {
+            "open": true,
+            "localName": "dialog"
+          },
+          "focused": {
+            "localName": "input",
+            "id": "owner-name",
+            "text": null,
+            "testField": true
+          }
+        },
+        "promptEscape": {
+          "prompt": {
+            "open": false,
+            "localName": "dialog"
+          },
+          "focused": {
+            "localName": "button",
+            "id": null,
+            "text": "Pet Owners",
+            "testField": false
+          }
+        },
+        "tabs": [
+          {
+            "text": "Details",
+            "active": "false",
+            "focused": false
+          },
+          {
+            "text": "Notes and preferences",
+            "active": "true",
+            "focused": true
+          }
+        ],
+        "failures": []
+      },
+      "media": {
+        "reducedMotion": true,
+        "forcedColors": true,
+        "dark": true,
+        "horizontalOverflow": 0,
+        "navVisible": true,
+        "failures": []
+      },
+      "performance": {
+        "samples": [
+          {
+            "domContentLoaded": 4,
+            "load": 6.800000011920929,
+            "firstContentfulPaint": 8,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 2,
+            "candidateTransferBytes": 233995,
+            "candidateEncodedBytes": 233395,
+            "candidateResourceDuration": 2.1000000834465027
+          },
+          {
+            "domContentLoaded": 12.099999964237213,
+            "load": 15.899999976158142,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 24,
+            "candidateRequestCount": 2,
+            "candidateTransferBytes": 233995,
+            "candidateEncodedBytes": 233395,
+            "candidateResourceDuration": 3.100000023841858
+          },
+          {
+            "domContentLoaded": 3.300000011920929,
+            "load": 5.200000047683716,
+            "firstContentfulPaint": 20,
+            "largestContentfulPaint": 20,
+            "candidateRequestCount": 2,
+            "candidateTransferBytes": 233995,
+            "candidateEncodedBytes": 233395,
+            "candidateResourceDuration": 1.3999999165534973
+          },
+          {
+            "domContentLoaded": 5.5,
+            "load": 8.800000011920929,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 2,
+            "candidateTransferBytes": 233995,
+            "candidateEncodedBytes": 233395,
+            "candidateResourceDuration": 2.100000023841858
+          },
+          {
+            "domContentLoaded": 5,
+            "load": 9.599999964237213,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 20,
+            "candidateRequestCount": 2,
+            "candidateTransferBytes": 233995,
+            "candidateEncodedBytes": 233395,
+            "candidateResourceDuration": 2.399999976158142
+          }
+        ],
+        "median": {
+          "domContentLoaded": 5,
+          "load": 8.800000011920929,
+          "firstContentfulPaint": 16,
+          "largestContentfulPaint": 20,
+          "candidateRequestCount": 2,
+          "candidateTransferBytes": 233995,
+          "candidateEncodedBytes": 233395,
+          "candidateResourceDuration": 2.1000000834465027
+        }
+      }
+    },
+    "webawesome": {
+      "screenshots": [
+        {
+          "case": "desktop-light-menu",
+          "file": "screenshots/webawesome-desktop-light-menu.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome&theme=light&state=menu-open&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(241, 242, 243)",
+            "bodyColor": "rgb(27, 29, 38)"
+          },
+          "failures": []
+        },
+        {
+          "case": "desktop-dark-prompt",
+          "file": "screenshots/webawesome-desktop-dark-prompt.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome&theme=dark&state=prompt&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "oklab(0.146735 0.000504202 -0.0117588)",
+            "bodyColor": "rgb(241, 242, 243)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-light-nav",
+          "file": "screenshots/webawesome-narrow-light-nav.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome&theme=light&state=responsive-nav&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(241, 242, 243)",
+            "bodyColor": "rgb(27, 29, 38)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-dark-all",
+          "file": "screenshots/webawesome-narrow-dark-all.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome&theme=dark&state=all&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "oklab(0.146735 0.000504202 -0.0117588)",
+            "bodyColor": "rgb(241, 242, 243)"
+          },
+          "failures": []
+        }
+      ],
+      "interactions": {
+        "menuOpened": {
+          "open": true,
+          "focused": false,
+          "trigger": "Pet Owners"
+        },
+        "menuEscape": {
+          "open": false,
+          "focused": true,
+          "trigger": "Pet Owners"
+        },
+        "actionSelection": {
+          "menu": {
+            "open": false,
+            "focused": false,
+            "trigger": "Pet Owners"
+          },
+          "prompt": {
+            "open": true,
+            "localName": "wa-dialog"
+          },
+          "focused": {
+            "localName": "wa-input",
+            "id": null,
+            "text": null,
+            "testField": true
+          }
+        },
+        "promptEscape": {
+          "prompt": {
+            "open": false,
+            "localName": "wa-dialog"
+          },
+          "focused": {
+            "localName": "wa-button",
+            "id": null,
+            "text": "Pet Owners",
+            "testField": false
+          }
+        },
+        "tabs": [
+          {
+            "text": "Details",
+            "active": false
+          },
+          {
+            "text": "Notes and preferences",
+            "active": true
+          }
+        ],
+        "failures": []
+      },
+      "media": {
+        "reducedMotion": true,
+        "forcedColors": true,
+        "dark": true,
+        "horizontalOverflow": 0,
+        "navVisible": true,
+        "failures": []
+      },
+      "performance": {
+        "samples": [
+          {
+            "domContentLoaded": 3.399999976158142,
+            "load": 11.300000011920929,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 24,
+            "candidateRequestCount": 132,
+            "candidateTransferBytes": 654833,
+            "candidateEncodedBytes": 615233,
+            "candidateResourceDuration": 586.1999998092651
+          },
+          {
+            "domContentLoaded": 6.800000011920929,
+            "load": 16.69999998807907,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 32,
+            "candidateRequestCount": 132,
+            "candidateTransferBytes": 654833,
+            "candidateEncodedBytes": 615233,
+            "candidateResourceDuration": 662.1999998092651
+          },
+          {
+            "domContentLoaded": 4.299999952316284,
+            "load": 12.399999976158142,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 32,
+            "candidateRequestCount": 132,
+            "candidateTransferBytes": 654833,
+            "candidateEncodedBytes": 615233,
+            "candidateResourceDuration": 714.299999833107
+          },
+          {
+            "domContentLoaded": 8,
+            "load": 17.099999964237213,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 36,
+            "candidateRequestCount": 132,
+            "candidateTransferBytes": 654833,
+            "candidateEncodedBytes": 615233,
+            "candidateResourceDuration": 537.799999833107
+          },
+          {
+            "domContentLoaded": 3.299999952316284,
+            "load": 9.5,
+            "firstContentfulPaint": 8,
+            "largestContentfulPaint": 20,
+            "candidateRequestCount": 132,
+            "candidateTransferBytes": 654833,
+            "candidateEncodedBytes": 615233,
+            "candidateResourceDuration": 551.1999996900558
+          }
+        ],
+        "median": {
+          "domContentLoaded": 4.299999952316284,
+          "load": 12.399999976158142,
+          "firstContentfulPaint": 12,
+          "largestContentfulPaint": 32,
+          "candidateRequestCount": 132,
+          "candidateTransferBytes": 654833,
+          "candidateEncodedBytes": 615233,
+          "candidateResourceDuration": 586.1999998092651
+        }
+      }
+    },
+    "openprops": {
+      "screenshots": [
+        {
+          "case": "desktop-light-menu",
+          "file": "screenshots/openprops-desktop-light-menu.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops&theme=light&state=menu-open&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(241, 243, 245)",
+            "bodyColor": "rgb(33, 37, 41)"
+          },
+          "failures": []
+        },
+        {
+          "case": "desktop-dark-prompt",
+          "file": "screenshots/openprops-desktop-dark-prompt.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops&theme=dark&state=prompt&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(52, 58, 64)",
+            "bodyColor": "rgb(241, 243, 245)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-light-nav",
+          "file": "screenshots/openprops-narrow-light-nav.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops&theme=light&state=responsive-nav&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(241, 243, 245)",
+            "bodyColor": "rgb(33, 37, 41)"
+          },
+          "failures": []
+        },
+        {
+          "case": "narrow-dark-all",
+          "file": "screenshots/openprops-narrow-dark-all.jpg",
+          "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops&theme=dark&state=all&motion=normal",
+          "audit": {
+            "title": "Causeway theming prototype",
+            "heading": "Mary Smith and a deliberately long contextual title",
+            "duplicateIds": [],
+            "unlabeledButtons": [],
+            "unlabeledInputs": [],
+            "dialogsWithoutName": [],
+            "hiddenFocus": false,
+            "horizontalOverflow": 0,
+            "fixtureReady": "true",
+            "bodyBackground": "rgb(52, 58, 64)",
+            "bodyColor": "rgb(241, 243, 245)"
+          },
+          "failures": []
+        }
+      ],
+      "interactions": {
+        "menuOpened": {
+          "open": true,
+          "ariaExpanded": "true",
+          "panelHidden": false,
+          "panelDisplay": "grid",
+          "focused": true
+        },
+        "menuEscape": {
+          "open": false,
+          "ariaExpanded": "false",
+          "panelHidden": false,
+          "panelDisplay": "none",
+          "focused": true
+        },
+        "actionSelection": {
+          "menu": {
+            "open": false,
+            "ariaExpanded": "false",
+            "panelHidden": false,
+            "panelDisplay": "none",
+            "focused": false
+          },
+          "prompt": {
+            "open": true,
+            "localName": "dialog"
+          },
+          "focused": {
+            "localName": "input",
+            "id": "owner-name",
+            "text": null,
+            "testField": true
+          }
+        },
+        "promptEscape": {
+          "prompt": {
+            "open": false,
+            "localName": "dialog"
+          },
+          "focused": {
+            "localName": "button",
+            "id": null,
+            "text": "Pet Owners",
+            "testField": false
+          }
+        },
+        "tabs": [
+          {
+            "text": "Details",
+            "active": "false",
+            "focused": false
+          },
+          {
+            "text": "Notes and preferences",
+            "active": "true",
+            "focused": true
+          }
+        ],
+        "failures": []
+      },
+      "media": {
+        "reducedMotion": true,
+        "forcedColors": true,
+        "dark": true,
+        "horizontalOverflow": 0,
+        "navVisible": true,
+        "failures": []
+      },
+      "performance": {
+        "samples": [
+          {
+            "domContentLoaded": 4.199999988079071,
+            "load": 6.099999964237213,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 52147,
+            "candidateEncodedBytes": 50947,
+            "candidateResourceDuration": 3.6000000834465027
+          },
+          {
+            "domContentLoaded": 6.099999964237213,
+            "load": 8.099999964237213,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 52147,
+            "candidateEncodedBytes": 50947,
+            "candidateResourceDuration": 4.200000047683716
+          },
+          {
+            "domContentLoaded": 3.199999988079071,
+            "load": 4.699999988079071,
+            "firstContentfulPaint": 12,
+            "largestContentfulPaint": 12,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 52147,
+            "candidateEncodedBytes": 50947,
+            "candidateResourceDuration": 2.8999999165534973
+          },
+          {
+            "domContentLoaded": 3.899999976158142,
+            "load": 5.699999988079071,
+            "firstContentfulPaint": 8,
+            "largestContentfulPaint": 20,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 52147,
+            "candidateEncodedBytes": 50947,
+            "candidateResourceDuration": 2.5
+          },
+          {
+            "domContentLoaded": 5.799999952316284,
+            "load": 7.799999952316284,
+            "firstContentfulPaint": 16,
+            "largestContentfulPaint": 16,
+            "candidateRequestCount": 4,
+            "candidateTransferBytes": 52147,
+            "candidateEncodedBytes": 50947,
+            "candidateResourceDuration": 3.900000035762787
+          }
+        ],
+        "median": {
+          "domContentLoaded": 4.199999988079071,
+          "load": 6.099999964237213,
+          "firstContentfulPaint": 16,
+          "largestContentfulPaint": 16,
+          "candidateRequestCount": 4,
+          "candidateTransferBytes": 52147,
+          "candidateEncodedBytes": 50947,
+          "candidateResourceDuration": 3.6000000834465027
+        }
+      }
+    }
+  },
+  "notes": [
+    "Screenshots use identical fixture content and deterministic viewport, theme, and state parameters.",
+    "DOM audits are bounded structural checks and do not replace Lighthouse or manual assistive-technology review.",
+    "Performance values are local medians after one warm-up and exclude shared harness files from candidate transfer summaries."
+  ]
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/browser-evidence.md b/openspec/changes/analyze-web-component-theming-kit/evidence/results/browser-evidence.md
new file mode 100644
index 00000000000..461fc587906
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/browser-evidence.md
@@ -0,0 +1,52 @@
+# Browser evidence summary
+
+Generated: 2026-08-20T23:16:09.223Z
+
+## baseline
+
+- Screenshots: 4.
+- Browser failures: 0.
+- Horizontal overflow: 0, 0, 0, 0 pixels.
+- Menu opened: {"open":true,"ariaExpanded":"true","panelHidden":false,"panelDisplay":"block","focused":true}.
+- Menu after Escape: {"open":false,"ariaExpanded":"false","panelHidden":true,"panelDisplay":"none","focused":true}.
+- Action selection: {"menu":{"open":false,"ariaExpanded":"false","panelHidden":true,"panelDisplay":"none","focused":false},"prompt":{"open":true,"localName":"dialog"},"focused":{"localName":"input","id":"owner-name","text":null,"testField":true}}.
+- Prompt after Escape: {"prompt":{"open":false,"localName":"dialog"},"focused":{"localName":"button","id":null,"text":"Pet Owners","testField":false}}.
+- Reduced motion / forced colors: true / true.
+- Median performance: {"domContentLoaded":6.100000023841858,"load":7.900000035762787,"firstContentfulPaint":16,"largestContentfulPaint":16,"candidateRequestCount":4,"candidateTransferBytes":37255,"candidateEncodedBytes":36055,"candidateResourceDuration":5.199999988079071}.
+
+## bootstrap
+
+- Screenshots: 4.
+- Browser failures: 0.
+- Horizontal overflow: 0, 0, 0, 0 pixels.
+- Menu opened: {"open":true,"ariaExpanded":"true","panelHidden":false,"panelDisplay":"block","focused":true}.
+- Menu after Escape: {"open":false,"ariaExpanded":"false","panelHidden":true,"panelDisplay":"none","focused":true}.
+- Action selection: {"menu":{"open":false,"ariaExpanded":"false","panelHidden":true,"panelDisplay":"none","focused":false},"prompt":{"open":true,"localName":"dialog"},"focused":{"localName":"input","id":"owner-name","text":null,"testField":true}}.
+- Prompt after Escape: {"prompt":{"open":false,"localName":"dialog"},"focused":{"localName":"button","id":null,"text":"Pet Owners","testField":false}}.
+- Reduced motion / forced colors: true / true.
+- Median performance: {"domContentLoaded":5,"load":8.800000011920929,"firstContentfulPaint":16,"largestContentfulPaint":20,"candidateRequestCount":2,"candidateTransferBytes":233995,"candidateEncodedBytes":233395,"candidateResourceDuration":2.1000000834465027}.
+
+## webawesome
+
+- Screenshots: 4.
+- Browser failures: 0.
+- Horizontal overflow: 0, 0, 0, 0 pixels.
+- Menu opened: {"open":true,"focused":false,"trigger":"Pet Owners"}.
+- Menu after Escape: {"open":false,"focused":true,"trigger":"Pet Owners"}.
+- Action selection: {"menu":{"open":false,"focused":false,"trigger":"Pet Owners"},"prompt":{"open":true,"localName":"wa-dialog"},"focused":{"localName":"wa-input","id":null,"text":null,"testField":true}}.
+- Prompt after Escape: {"prompt":{"open":false,"localName":"wa-dialog"},"focused":{"localName":"wa-button","id":null,"text":"Pet Owners","testField":false}}.
+- Reduced motion / forced colors: true / true.
+- Median performance: {"domContentLoaded":4.299999952316284,"load":12.399999976158142,"firstContentfulPaint":12,"largestContentfulPaint":32,"candidateRequestCount":132,"candidateTransferBytes":654833,"candidateEncodedBytes":615233,"candidateResourceDuration":586.1999998092651}.
+
+## openprops
+
+- Screenshots: 4.
+- Browser failures: 0.
+- Horizontal overflow: 0, 0, 0, 0 pixels.
+- Menu opened: {"open":true,"ariaExpanded":"true","panelHidden":false,"panelDisplay":"grid","focused":true}.
+- Menu after Escape: {"open":false,"ariaExpanded":"false","panelHidden":false,"panelDisplay":"none","focused":true}.
+- Action selection: {"menu":{"open":false,"ariaExpanded":"false","panelHidden":false,"panelDisplay":"none","focused":false},"prompt":{"open":true,"localName":"dialog"},"focused":{"localName":"input","id":"owner-name","text":null,"testField":true}}.
+- Prompt after Escape: {"prompt":{"open":false,"localName":"dialog"},"focused":{"localName":"button","id":null,"text":"Pet Owners","testField":false}}.
+- Reduced motion / forced colors: true / true.
+- Median performance: {"domContentLoaded":4.199999988079071,"load":6.099999964237213,"firstContentfulPaint":16,"largestContentfulPaint":16,"candidateRequestCount":4,"candidateTransferBytes":52147,"candidateEncodedBytes":50947,"candidateResourceDuration":3.6000000834465027}.
+
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/lighthouse-summary.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/lighthouse-summary.json
new file mode 100644
index 00000000000..c669037dd5f
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/lighthouse-summary.json
@@ -0,0 +1,535 @@
+{
+  "generatedAt": "2026-08-20T22:30:55.586Z",
+  "reports": {
+    "baseline-desktop": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline&theme=light&state=all",
+      "fetchTime": "2026-08-20T21:40:18.308Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 96,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "color-contrast",
+          "title": "Background and foreground colors do not have a sufficient contrast ratio.",
+          "score": 0,
+          "displayValue": null,
+          "items": [
+            {
+              "selector": "div.fixture-app > header.fixture-header > div.fixture-navbar > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-nav-trigger=\"\" aria-expanded=\"false\" aria-controls=\"fixture-navigation\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-menu-trigger=\"\" aria-expanded=\"false\" aria-controls=\"pet-owner-menu\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-object-action=\"update\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"danger\" type=\"button\" data-object-action=\"delete\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"details-tab\" aria-selected=\"true\" aria-controls=\"details-panel\" data-tab=\"details-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"notes-tab\" aria-selected=\"false\" aria-controls=\"notes-panel\" data-tab=\"notes-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            }
+          ]
+        },
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "baseline-mobile": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=baseline&theme=dark&state=responsive-nav&motion=reduce",
+      "fetchTime": "2026-08-20T21:40:36.282Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 96,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "color-contrast",
+          "title": "Background and foreground colors do not have a sufficient contrast ratio.",
+          "score": 0,
+          "displayValue": null,
+          "items": [
+            {
+              "selector": "div.fixture-app > header.fixture-header > div.fixture-navbar > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-nav-trigger=\"\" aria-expanded=\"true\" aria-controls=\"fixture-navigation\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-menu-trigger=\"\" aria-expanded=\"true\" aria-controls=\"pet-owner-menu\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"primary\" type=\"button\" data-menu-action=\"create\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.08 (foreground color: #121212, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-menu-action=\"find\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-menu-action=\"long\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-object-action=\"update\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"primary\" type=\"button\" data-object-action=\"book\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.08 (foreground color: #121212, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"danger\" type=\"button\" data-object-action=\"delete\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"details-tab\" aria-selected=\"true\" aria-controls=\"details-panel\" data-tab=\"details-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"notes-tab\" aria-selected=\"false\" aria-controls=\"notes-panel\" data-tab=\"notes-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"primary\" type=\"button\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.08 (foreground color: #121212, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            }
+          ]
+        },
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "bootstrap-desktop": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap&theme=light&state=all",
+      "fetchTime": "2026-08-20T21:40:57.748Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 96,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "color-contrast",
+          "title": "Background and foreground colors do not have a sufficient contrast ratio.",
+          "score": 0,
+          "displayValue": null,
+          "items": [
+            {
+              "selector": "div.fixture-app > header.fixture-header > div.fixture-navbar > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-nav-trigger=\"\" aria-expanded=\"false\" aria-controls=\"fixture-navigation\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-menu-trigger=\"\" aria-expanded=\"false\" aria-controls=\"pet-owner-menu\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-heading-row > div > p > code",
+              "snippet": "<code>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 4.26 (foreground color: #d63384, background color: #f8f9fa, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-object-action=\"update\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 4.44 (foreground color: #6c757d, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            }
+          ]
+        },
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "bootstrap-mobile": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=bootstrap&theme=dark&state=responsive-nav&motion=reduce",
+      "fetchTime": "2026-08-20T21:41:20.181Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 96,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "color-contrast",
+          "title": "Background and foreground colors do not have a sufficient contrast ratio.",
+          "score": 0,
+          "displayValue": null,
+          "items": [
+            {
+              "selector": "div.fixture-app > header.fixture-header > div.fixture-navbar > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-nav-trigger=\"\" aria-expanded=\"true\" aria-controls=\"fixture-navigation\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-menu-trigger=\"\" aria-expanded=\"true\" aria-controls=\"pet-owner-menu\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-menu-action=\"find\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-menu-action=\"long\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" aria-expanded=\"false\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" data-object-action=\"update\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.83 (foreground color: #6c757d, background color: #2b3035, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"details-tab\" aria-selected=\"true\" aria-controls=\"details-panel\" data-tab=\"details-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"notes-tab\" aria-selected=\"false\" aria-controls=\"notes-panel\" data-tab=\"notes-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.btn",
+              "snippet": "<button class=\"btn btn-outline-secondary\" data-kind=\"secondary\" type=\"button\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            }
+          ]
+        },
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "openprops-desktop": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops&theme=light&state=all",
+      "fetchTime": "2026-08-20T21:42:19.198Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 100,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "openprops-mobile": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=openprops&theme=dark&state=responsive-nav&motion=reduce",
+      "fetchTime": "2026-08-20T21:42:36.175Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 96,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "color-contrast",
+          "title": "Background and foreground colors do not have a sufficient contrast ratio.",
+          "score": 0,
+          "displayValue": null,
+          "items": [
+            {
+              "selector": "header.fixture-header > div.fixture-navbar > a.fixture-brand > span",
+              "snippet": "<span>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"primary\" type=\"button\" data-menu-action=\"create\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-heading-row > div > p > code",
+              "snippet": "<code>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.06 (foreground color: #f1f3f5, background color: #e9ecef, font size: 13.2pt (17.6px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" data-object-action=\"update\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"primary\" type=\"button\" data-object-action=\"book\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"danger\" type=\"button\" data-object-action=\"delete\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"details-tab\" aria-selected=\"true\" aria-controls=\"details-panel\" data-tab=\"details-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\" role=\"tab\" id=\"notes-tab\" aria-selected=\"false\" aria-controls=\"notes-panel\" data-tab=\"notes-panel\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-tabs > div#details-panel > div.fixture-properties > a",
+              "snippet": "<a href=\"mailto:mary@example.test\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 3.09 (foreground color: #4263eb, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"primary\" type=\"button\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button",
+              "snippet": "<button class=\"fixture-button\" data-kind=\"secondary\" type=\"button\">",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > thead > tr > th",
+              "snippet": "<th>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > thead > tr > th",
+              "snippet": "<th>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > thead > tr > th",
+              "snippet": "<th>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > thead > tr > th",
+              "snippet": "<th>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > thead > tr > th",
+              "snippet": "<th>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > thead > tr > th",
+              "snippet": "<th>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            },
+            {
+              "selector": "table.fixture-table > tbody > tr > td",
+              "snippet": "<td>",
+              "explanation": "Fix any of the following:\n  Element has insufficient color contrast of 1.05 (foreground color: #f1f3f5, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1"
+            }
+          ]
+        },
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "webawesome-desktop": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome&theme=light&state=all",
+      "fetchTime": "2026-08-20T21:41:35.255Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 100,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    },
+    "webawesome-mobile": {
+      "url": "http://127.0.0.1:4173/openspec/changes/analyze-web-component-theming-kit/evidence/harness/prototype.html?candidate=webawesome&theme=dark&state=responsive-nav&motion=reduce",
+      "fetchTime": "2026-08-20T21:41:53.719Z",
+      "lighthouseVersion": "13.1.0",
+      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
+      "categories": {
+        "accessibility": 100,
+        "best-practices": 100,
+        "seo": 90
+      },
+      "failedAudits": [
+        {
+          "id": "meta-description",
+          "title": "Document does not have a meta description",
+          "score": 0,
+          "displayValue": null,
+          "items": []
+        }
+      ]
+    }
+  }
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/lighthouse-summary.md b/openspec/changes/analyze-web-component-theming-kit/evidence/results/lighthouse-summary.md
new file mode 100644
index 00000000000..12a61c2c8e1
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/lighthouse-summary.md
@@ -0,0 +1,106 @@
+# Lighthouse summary
+
+Generated: 2026-08-20T22:30:55.586Z
+
+## baseline-desktop
+
+- Scores: accessibility 96, best-practices 100, seo 90.
+- Failed audits: 2.
+- color-contrast: Background and foreground colors do not have a sufficient contrast ratio.; affected items 9.
+  - div.fixture-app > header.fixture-header > div.fixture-navbar > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+- meta-description: Document does not have a meta description; affected items 0.
+
+## baseline-mobile
+
+- Scores: accessibility 96, best-practices 100, seo 90.
+- Failed audits: 2.
+- color-contrast: Background and foreground colors do not have a sufficient contrast ratio.; affected items 14.
+  - div.fixture-app > header.fixture-header > div.fixture-navbar > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 3.08 (foreground color: #121212, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 3.08 (foreground color: #121212, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab: Fix any of the following:   Element has insufficient color contrast of 2.41 (foreground color: #1f2937, background color: #18704a, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+- meta-description: Document does not have a meta description; affected items 0.
+
+## bootstrap-desktop
+
+- Scores: accessibility 96, best-practices 100, seo 90.
+- Failed audits: 2.
+- color-contrast: Background and foreground colors do not have a sufficient contrast ratio.; affected items 6.
+  - div.fixture-app > header.fixture-header > div.fixture-navbar > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-heading-row > div > p > code: Fix any of the following:   Element has insufficient color contrast of 4.26 (foreground color: #d63384, background color: #f8f9fa, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.btn: Fix any of the following:   Element has insufficient color contrast of 4.44 (foreground color: #6c757d, background color: #f8f9fa, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+- meta-description: Document does not have a meta description; affected items 0.
+
+## bootstrap-mobile
+
+- Scores: accessibility 96, best-practices 100, seo 90.
+- Failed audits: 2.
+- color-contrast: Background and foreground colors do not have a sufficient contrast ratio.; affected items 10.
+  - div.fixture-app > header.fixture-header > div.fixture-navbar > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - nav#fixture-navigation > causeway-menubars.fixture-menus > div.fixture-menu > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.btn: Fix any of the following:   Element has insufficient color contrast of 2.83 (foreground color: #6c757d, background color: #2b3035, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.btn: Fix any of the following:   Element has insufficient color contrast of 3.28 (foreground color: #6c757d, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+- meta-description: Document does not have a meta description; affected items 0.
+
+## openprops-desktop
+
+- Scores: accessibility 100, best-practices 100, seo 90.
+- Failed audits: 1.
+- meta-description: Document does not have a meta description; affected items 0.
+
+## openprops-mobile
+
+- Scores: accessibility 96, best-practices 100, seo 90.
+- Failed audits: 2.
+- color-contrast: Background and foreground colors do not have a sufficient contrast ratio.; affected items 27.
+  - header.fixture-header > div.fixture-navbar > a.fixture-brand > span: Fix any of the following:   Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1
+  - causeway-menubars.fixture-menus > div.fixture-menu > div#pet-owner-menu > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-heading-row > div > p > code: Fix any of the following:   Element has insufficient color contrast of 1.06 (foreground color: #f1f3f5, background color: #e9ecef, font size: 13.2pt (17.6px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - main#fixture-main > div.fixture-heading-row > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#details-tab: Fix any of the following:   Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - causeway-object.fixture-card > div.fixture-tabs > div > button#notes-tab: Fix any of the following:   Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-tabs > div#details-panel > div.fixture-properties > a: Fix any of the following:   Element has insufficient color contrast of 3.09 (foreground color: #4263eb, background color: #212529, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 2.96 (foreground color: #ffffff, background color: #748ffc, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - div.fixture-page-grid > aside.fixture-card > div.fixture-actions > button.fixture-button: Fix any of the following:   Element has insufficient color contrast of 1.11 (foreground color: #f1f3f5, background color: #ffffff, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
+  - table.fixture-table > thead > tr > th: Fix any of the following:   Element has insufficient color contrast of 1.77 (foreground color: #030507, background color: #343a40, font size: 12.0pt (16px), font weight: bold). Expected contrast ratio of 4.5:1
+- meta-description: Document does not have a meta description; affected items 0.
+
+## webawesome-desktop
+
+- Scores: accessibility 100, best-practices 100, seo 90.
+- Failed audits: 1.
+- meta-description: Document does not have a meta description; affected items 0.
+
+## webawesome-mobile
+
+- Scores: accessibility 100, best-practices 100, seo 90.
+- Failed audits: 1.
+- meta-description: Document does not have a meta description; affected items 0.
+
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/packaging-verification.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/packaging-verification.json
new file mode 100644
index 00000000000..51de9c4cae3
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/packaging-verification.json
@@ -0,0 +1,15 @@
+{
+  "generatedAt": "2026-08-20",
+  "command": "mvn -f evidence/packaging/pom.xml package",
+  "buildResult": "success",
+  "jar": "evidence/packaging/target/causeway-theming-candidate-assets-1.0.0-analysis.jar",
+  "jarBytes": 226029,
+  "entryCount": 26,
+  "packagedFiles": {
+    "META-INF/resources/causeway-toolkit/bootstrap/": 2,
+    "META-INF/resources/causeway-toolkit/webawesome/": 2,
+    "META-INF/resources/causeway-toolkit/open-props/": 3,
+    "META-INF/licenses/": 3
+  },
+  "interpretation": "All frozen candidates can be packaged offline in a standard Maven JAR after pinned npm acquisition. Web Awesome is represented by reproducible selective JavaScript and CSS bundles rather than its complete browser distribution."
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/real-viewer-integration.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/real-viewer-integration.json
new file mode 100644
index 00000000000..42f3b259006
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/real-viewer-integration.json
@@ -0,0 +1,171 @@
+{
+  "generatedAt": "2026-08-20T23:03:36.077Z",
+  "petclinicOrigin": "http://127.0.0.1:8080",
+  "harnessOrigin": "http://127.0.0.1:4173",
+  "candidates": {
+    "baseline": {
+      "mode": "unmodified real viewer",
+      "before": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "4px",
+          "font": "Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"
+        }
+      },
+      "after": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "4px",
+          "font": "Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"
+        }
+      },
+      "menuOpened": "true",
+      "menuClosed": "false",
+      "screenshot": "screenshots/integration-baseline.jpg",
+      "failures": []
+    },
+    "bootstrap": {
+      "mode": "candidate CSS injected into the real viewer without source changes",
+      "before": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "4px",
+          "font": "Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"
+        }
+      },
+      "after": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "0px",
+          "font": "system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\""
+        }
+      },
+      "menuOpened": "true",
+      "menuClosed": "false",
+      "screenshot": "screenshots/integration-bootstrap.jpg",
+      "failures": []
+    },
+    "webawesome": {
+      "mode": "single internal adapter plus toolkit theme loaded beside the real viewer",
+      "before": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "4px",
+          "font": "Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"
+        }
+      },
+      "after": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 9,
+        "analysisAdapterCount": 1,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "4px",
+          "font": "system-ui, sans-serif"
+        }
+      },
+      "menuOpened": "true",
+      "menuClosed": "false",
+      "screenshot": "screenshots/integration-webawesome.jpg",
+      "failures": []
+    },
+    "openprops": {
+      "mode": "candidate CSS injected into the real viewer without source changes",
+      "before": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "4px",
+          "font": "Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"
+        }
+      },
+      "after": {
+        "url": "http://127.0.0.1:8080/htmx/object/petclinic.HomePage/AAAgbu82rFaWU0_8Sy8J6QHFqKUIMApOunjgDZQBc-r8WA6s7QAFc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA_QAAAAAAAAHcIAAAAEAAAAAB4",
+        "title": "4 pet owners · 3 upcoming visits · Pet Clinic",
+        "causewayElementCount": 7,
+        "analysisAdapterCount": 0,
+        "horizontalOverflow": 0,
+        "routeState": "ready",
+        "menuState": "ready",
+        "bodyBackground": "rgb(243, 245, 248)",
+        "bodyColor": "rgb(31, 41, 55)",
+        "firstButton": {
+          "color": "rgb(31, 41, 55)",
+          "background": "rgb(255, 255, 255)",
+          "borderRadius": "5px",
+          "font": "system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif"
+        }
+      },
+      "menuOpened": "true",
+      "menuClosed": "false",
+      "screenshot": "screenshots/integration-openprops.jpg",
+      "failures": []
+    }
+  }
+}
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/real-viewer-integration.md b/openspec/changes/analyze-web-component-theming-kit/evidence/results/real-viewer-integration.md
new file mode 100644
index 00000000000..0f1dc08662a
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/real-viewer-integration.md
@@ -0,0 +1,57 @@
+# Real Petclinic integration check
+
+Generated: 2026-08-20T23:03:36.077Z
+
+The check injects candidate assets only into a disposable browser context and does not modify production files.
+
+## baseline
+
+- Mode: unmodified real viewer.
+- Browser failures: 0.
+- Menu disclosure: true before Escape, false after Escape.
+- Horizontal overflow before/after: 0/0 pixels.
+- Body background before/after: rgb(243, 245, 248)/rgb(243, 245, 248).
+- First button before/after: {"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"4px","font":"Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"}/{"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"4px","font":"Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"}.
+- Internal analysis adapters: 0.
+- Screenshot: screenshots/integration-baseline.jpg.
+
+## bootstrap
+
+- Mode: candidate CSS injected into the real viewer without source changes.
+- Browser failures: 0.
+- Menu disclosure: true before Escape, false after Escape.
+- Horizontal overflow before/after: 0/0 pixels.
+- Body background before/after: rgb(243, 245, 248)/rgb(243, 245, 248).
+- First button before/after: {"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"4px","font":"Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"}/{"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"0px","font":"system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto [...]
+- Internal analysis adapters: 0.
+- Screenshot: screenshots/integration-bootstrap.jpg.
+
+## webawesome
+
+- Mode: single internal adapter plus toolkit theme loaded beside the real viewer.
+- Browser failures: 0.
+- Menu disclosure: true before Escape, false after Escape.
+- Horizontal overflow before/after: 0/0 pixels.
+- Body background before/after: rgb(243, 245, 248)/rgb(243, 245, 248).
+- First button before/after: {"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"4px","font":"Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"}/{"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"4px","font":"system-ui, sans-serif"}.
+- Internal analysis adapters: 1.
+- Screenshot: screenshots/integration-webawesome.jpg.
+
+## openprops
+
+- Mode: candidate CSS injected into the real viewer without source changes.
+- Browser failures: 0.
+- Menu disclosure: true before Escape, false after Escape.
+- Horizontal overflow before/after: 0/0 pixels.
+- Body background before/after: rgb(243, 245, 248)/rgb(243, 245, 248).
+- First button before/after: {"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"4px","font":"Inter, ui-sans-serif, system-ui, -apple-system, \"system-ui\", \"Segoe UI\", sans-serif"}/{"color":"rgb(31, 41, 55)","background":"rgb(255, 255, 255)","borderRadius":"5px","font":"system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif"}.
+- Internal analysis adapters: 0.
+- Screenshot: screenshots/integration-openprops.jpg.
+
+## Interpretation
+
+- Bootstrap and Open Props can affect the existing light DOM immediately through global CSS, which makes incremental styling easy but creates collision and regression risk.
+- Web Awesome does not style existing Causeway controls automatically because its visual implementation lives behind toolkit-owned custom elements and shadow DOM.
+- Web Awesome therefore requires explicit internal adapters or component renderer changes, while Bootstrap and Open Props can begin as token or stylesheet changes.
+- All injected strategies preserved route readiness, menu readiness, Escape dismissal, and page-level overflow in this bounded check.
+
diff --git a/openspec/changes/analyze-web-component-theming-kit/evidence/results/security-audit.json b/openspec/changes/analyze-web-component-theming-kit/evidence/results/security-audit.json
new file mode 100644
index 00000000000..fc4062a31f8
--- /dev/null
+++ b/openspec/changes/analyze-web-component-theming-kit/evidence/results/security-audit.json
@@ -0,0 +1,24 @@
+{
+  "generatedAt": "2026-08-20",
+  "commands": [
+    "npm audit --omit=dev --json",
+    "npm audit --json"
+  ],
+  "production": {
+    "info": 0,
+    "low": 0,
+    "moderate": 0,
+    "high": 0,
+    "critical": 0,
+    "total": 0
+  },
+  "completeHarness": {
+    "info": 0,
+    "low": 0,
+    "moderate": 0,
+    "high": 0,
... 171 lines suppressed ...