You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/10/07 20:17:01 UTC

(juneau) branch master updated: Petstore console Plan B: custom About, Dev, Secure, Themes and Flavors pages, console asset and WebJars vendor routes, browser and coverage test suites, and the console-ui AssetResolutionAssert test helper; add the application/jsonlines and application/json5lines media-type aliases and echo the negotiated NDJSON type from ReactiveResponseProcessor; Sonar fixes and suppressions across the views JS, the petstore console and RemoteClient; limit the CI JS-harness step to the *_BrowserTest suites.

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

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new d5b1a987ac Petstore console Plan B: custom About, Dev, Secure, Themes and Flavors pages, console asset and WebJars vendor routes, browser and coverage test suites, and the console-ui AssetResolutionAssert test helper; add the application/jsonlines and application/json5lines media-type aliases and echo the negotiated NDJSON type from ReactiveResponseProcessor; Sonar fixes and suppressions across the views JS, the petstore console and RemoteClient; limit the CI JS-harness step to t [...]
d5b1a987ac is described below

commit d5b1a987ac96c2019d084c079cba740305894390
Author: James Bognar <ja...@apache.org>
AuthorDate: Wed Oct 7 16:16:54 2026 -0400

    Petstore console Plan B: custom About, Dev, Secure, Themes and Flavors pages, console asset and WebJars vendor routes, browser and coverage test suites, and the console-ui AssetResolutionAssert test helper; add the application/jsonlines and application/json5lines media-type aliases and echo the negotiated NDJSON type from ReactiveResponseProcessor; Sonar fixes and suppressions across the views JS, the petstore console and RemoteClient; limit the CI JS-harness step to the *_BrowserTest [...]
---
 .github/workflows/maven.yml                        |  35 +++-
 .../juneau/marshall/json5l/Json5lParser.java       |   4 +-
 .../juneau/marshall/json5l/Json5lSerializer.java   |   4 +-
 .../apache/juneau/marshall/jsonl/JsonlParser.java  |   4 +-
 .../juneau/marshall/jsonl/JsonlSerializer.java     |   4 +-
 .../marshall/json5l/Json5lMediaType_Test.java      |  27 +++
 .../juneau/marshall/jsonl/JsonlMediaType_Test.java |  25 +++
 .../server/JsonLinesMediaTypeAliases_Test.java     | 110 ++++++++++
 juneau-petstore/juneau-petstore-core/pom.xml       | 139 ++++++++++++-
 .../apache/juneau/petstore/console/AboutRest.java  |  97 +++++++++
 .../juneau/petstore/console/ConsoleAssetsRest.java |  48 +++++
 .../juneau/petstore/console/ConsoleStubs.java      |  13 +-
 .../juneau/petstore/console/ConsoleWrites.java     |  10 +-
 .../petstore/console/PetstoreConsolePage.java      |  10 +-
 .../petstore/console/PetstoreConsoleResource.java  |   9 +-
 .../apache/juneau/petstore/console/StoreRest.java  |   7 +-
 .../apache/juneau/petstore/console/VendorRest.java |  69 ++++++
 .../juneau/petstore/console/dev/DevRest.java       |  63 ++++++
 .../juneau/petstore/console/dev/SecureApiRest.java | 105 ++++++++++
 .../juneau/petstore/console/dev/SecureRest.java    |  76 +++++++
 .../juneau/petstore/console/dev/ThemesRest.java    |  91 ++++++++
 .../petstore/console/dev/flavors/Flavor.java       |  93 +++++++++
 .../petstore/console/dev/flavors/FlavorPets.java   |  66 ++++++
 .../OpsRest.java => dev/flavors/FlavorsRest.java}  |  44 ++--
 .../flavors/FreemarkerFlavorRest.java}             |  35 ++--
 .../console/dev/flavors/HtmlFlavorRest.java        |  61 ++++++
 .../console/dev/flavors/MustacheFlavorRest.java    |  69 ++++++
 .../console/dev/flavors}/package-info.java         |  14 +-
 .../juneau/petstore/console/dev}/package-info.java |  15 +-
 .../juneau/petstore/console/ops/AuditRest.java     |   7 +-
 .../juneau/petstore/console/ops/JobsRest.java      |  21 +-
 .../juneau/petstore/console/ops/OpsRest.java       |   3 +
 .../juneau/petstore/console/pets/PetsRest.java     |  24 +--
 .../java/org/apache/juneau/petstore/dto/Pet.java   |  48 +++++
 .../petstore/rest/PetFreemarkerViewResource.java   |  99 ---------
 .../juneau/petstore/rest/PetHtmlResource.java      | 122 -----------
 .../juneau/petstore/rest/PetInfoResource.java      |  93 ---------
 .../petstore/rest/PetMustacheViewResource.java     | 108 ----------
 .../juneau/petstore/rest/PetSecureResource.java    | 152 --------------
 .../juneau/petstore/rest/PetstoreUiResource.java   |  19 ++
 .../apache/juneau/petstore/service/AuditEntry.java |   2 +-
 .../juneau/petstore/service/PendingChange.java     |   2 +-
 .../juneau/petstore/service/PendingChanges.java    |   6 +-
 .../apache/juneau/petstore/service/PetStore.java   |   6 +-
 .../apache/juneau/petstore/console/dev/secure.js   |  43 ++++
 .../petstore/console/fragments/pets.mustache}      |  23 +-
 .../juneau/petstore/console/templates/about.ftlh   |  44 ++++
 .../juneau/petstore/console/templates/base.ftlh    |  23 +-
 .../petstore/console/templates/flavors-react.ftlh} |  28 +--
 .../petstore/console/templates/flavors.ftlh}       |  27 +--
 .../console/templates/fragments/pets.ftlh}         |  28 +--
 .../juneau/petstore/console/templates/secure.ftlh  |  35 ++++
 .../juneau/petstore/console/templates/themes.ftlh  |  44 ++++
 .../juneau/petstore/PetstoreSharedStore_Test.java  |  15 +-
 .../juneau/petstore/console/AboutRest_Test.java    |  79 +++++++
 .../petstore/console/ConsoleWrites_Test.java       |  31 +++
 .../petstore/console/PetstoreAssetOrder_Test.java  |   3 +-
 .../petstore/console/PetstoreAssets_Test.java      |  98 +++++++++
 .../petstore/console/PetstoreConsoleFixture.java   |   3 +
 .../console/PetstoreConsoleShell_Test.java         |   9 +-
 .../petstore/console/PetstoreFailLoud_Test.java    |  36 ++--
 .../console/PetstorePages_ContractTest.java        |  13 +-
 .../juneau/petstore/console/browser/Covers.java    |  32 ++-
 .../console/browser/Harness_BrowserTest.java       |  55 +++++
 .../browser/PetstoreAsyncJob_BrowserTest.java      |  87 ++++++++
 .../petstore/console/browser/PetstoreBrowser.java  | 135 ++++++++++++
 .../console/browser/PetstoreCoverage_Test.java     | 118 +++++++++++
 .../browser/PetstoreDatatable_BrowserTest.java     |  91 ++++++++
 .../browser/PetstoreFlavors_BrowserTest.java       |  92 ++++++++
 .../console/browser/PetstoreNav_BrowserTest.java   |  74 +++++++
 .../browser/PetstoreSecure_BrowserTest.java        |  70 +++++++
 .../console/browser/PetstoreTestServer.java        | 108 ++++++++++
 .../browser/PetstoreThemes_BrowserTest.java        |  90 ++++++++
 .../juneau/petstore/console/dev/DevNav_Test.java   |  68 ++++++
 .../petstore/console/dev/SecureRest_Test.java      |  99 +++++++++
 .../petstore/console/dev/ThemesRest_Test.java      |  90 ++++++++
 .../console/dev/flavors/FlavorsRest_Test.java      | 103 +++++++++
 .../petstore/console/ops/AuditRest_Test.java       |  17 ++
 .../juneau/petstore/console/ops/JobsRest_Test.java |   1 +
 .../petstore/console/pets/PetsRest_Test.java       |   6 +-
 .../org/apache/juneau/petstore/dto/Pet_Test.java   |  64 ++++++
 .../rest/PetFreemarkerViewResource_Test.java       |  51 -----
 .../juneau/petstore/rest/PetHtmlResource_Test.java |  56 -----
 .../juneau/petstore/rest/PetInfoResource_Test.java |  58 ------
 .../rest/PetMustacheViewResource_Test.java         |  51 -----
 .../petstore/rest/PetstoreUiResource_Test.java     |   8 +
 .../juneau/petstore/sql/PetstoreSqlSchema.java     | 142 +++++++++++++
 .../juneau/petstore/sql/PetstoreSql_Test.java      | 129 ++++++++++++
 .../src/test/js/petstore-browser.cjs               | 113 ++++++++++
 .../juneau/petstore/jetty/RootResources.java       |  10 +-
 .../juneau/petstore/jetty/PetstoreJetty_Test.java  |  74 +++++--
 .../juneau/petstore/springboot/RootResources.java  |   8 +-
 .../springboot/PetstoreSpringboot_Test.java        |  68 ++++--
 .../juneau/rest/client/remote/RemoteClient.java    |   2 +-
 .../rest/client/RestClient_Negotiation_Test.java   |   2 +-
 .../org/apache/juneau/http/header/ContentType.java |   4 +
 .../juneau/http/header/ContentType_Test.java       |   2 +
 .../server/console/test/AssetResolutionAssert.java | 155 ++++++++++++++
 .../rest/server/console/test/package-info.java     |   5 +-
 .../console/test/AssetResolutionAssert_Test.java   | 116 +++++++++++
 .../server/reactive/ReactiveResponseProcessor.java |   3 +-
 .../reactive/ReactiveResponseProcessor_Test.java   |  14 ++
 .../org/apache/juneau/views/juneau-config.js       | 110 +++++-----
 .../org/apache/juneau/views/juneau-helpers.js      |  25 ++-
 .../org/apache/juneau/views/juneau-icons.js        |   2 +-
 .../org/apache/juneau/views/juneau-pagestate.js    |  12 +-
 .../org/apache/juneau/views/juneau-regions.js      |   8 +-
 .../org/apache/juneau/views/juneau-renders.js      |   8 +-
 .../org/apache/juneau/views/juneau-ribbon.js       |   8 +-
 .../org/apache/juneau/views/juneau-search.js       |  30 +--
 .../org/apache/juneau/views/juneau-urlstate.js     |  16 +-
 .../org/apache/juneau/views/juneau-views.js        | 231 +++++++++++----------
 .../rest/server/views/ChromeCss_NavRules_Test.java |   3 +
 .../rest/server/views/ViewsJs_SlotMount_Test.java  |   5 +
 .../src/test/js/async-job.cjs                      |  12 +-
 .../src/test/js/cell-popover.cjs                   |  10 +-
 .../src/test/js/chrome-tip.cjs                     |  14 +-
 .../src/test/js/client-column-search.cjs           |  12 +-
 .../src/test/js/column-search-popover.cjs          |   2 +-
 .../src/test/js/column-search.cjs                  |   6 +-
 .../src/test/js/config-application.cjs             |   2 +-
 .../src/test/js/config-chooser.cjs                 |   2 +-
 .../src/test/js/config-persistence.cjs             |   2 +-
 .../src/test/js/config-reinit.cjs                  |   4 +-
 .../src/test/js/console-dom-shim.cjs               |   7 +-
 .../src/test/js/datatables-ajax.cjs                |  10 +-
 .../src/test/js/detail-bar-slot-browser.cjs        |   2 +-
 .../src/test/js/detail-bar-slot.cjs                |  24 +--
 .../src/test/js/detail-field-grid-browser.cjs      |   2 +-
 .../src/test/js/detail-region-row-id.cjs           |   2 +-
 .../src/test/js/dialog-bar-slot.cjs                |  30 +--
 .../src/test/js/dialog-form-browser.cjs            |   6 +-
 .../src/test/js/dialog-receipt.cjs                 |  15 +-
 .../src/test/js/helpers-harness.cjs                |   2 +-
 .../src/test/js/helpers.cjs                        |  16 +-
 .../juneau-rest-server-views/src/test/js/icons.cjs |   9 +-
 .../src/test/js/juneau-console.cjs                 |   6 +-
 .../src/test/js/modal-result.cjs                   |  10 +-
 .../src/test/js/nested-popup-browser.cjs           |   2 +-
 .../src/test/js/nested-table.cjs                   |  13 +-
 .../src/test/js/page-state.cjs                     |   2 +-
 .../src/test/js/probe-group.cjs                    |   2 +-
 .../src/test/js/regions-barrier.cjs                |  11 +-
 .../src/test/js/regions-bus.cjs                    |   2 +-
 .../src/test/js/regions-declarative-default.cjs    |  10 +-
 .../src/test/js/regions-harness.cjs                |   2 +-
 .../src/test/js/regions-mount.cjs                  |   4 +-
 .../src/test/js/regions-primitive.cjs              |   6 +-
 .../src/test/js/renders.cjs                        |   2 +-
 .../src/test/js/ribbon-dialog.cjs                  |   4 +-
 .../src/test/js/ribbon-icon-fallback.cjs           |   2 +-
 .../src/test/js/ribbon-normalize.cjs               |   4 +-
 .../src/test/js/row-action-endpoint.cjs            |   6 +-
 .../src/test/js/row-action-form.cjs                |   2 +-
 .../src/test/js/row-actions.cjs                    |   4 +-
 .../src/test/js/row-detail.cjs                     |  22 +-
 .../src/test/js/row-selection-bulk.cjs             |   4 +-
 .../src/test/js/sectioned-dialog-browser.cjs       |   6 +-
 .../src/test/js/slot-mount.cjs                     |  32 ++-
 .../src/test/js/symbol-sprite-render.cjs           | 118 ++++++-----
 .../src/test/js/table-clip-free-browser.cjs        |   6 +-
 .../src/test/js/table-polling.cjs                  |   2 +-
 .../src/test/js/url-state-live.cjs                 |   8 +-
 .../src/test/js/url-state.cjs                      |   2 +-
 .../src/test/js/view-settings-a11y-browser.cjs     |   2 +-
 .../src/test/js/views-dom-shim.cjs                 |  57 +++--
 .../src/test/js/views-server-nested.cjs            |   8 +-
 .../server/converter/QueryableSettings_Test.java   |   3 +
 pom.xml                                            |   2 +
 169 files changed, 4590 insertions(+), 1537 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 478d229e77..53a7f31f47 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -96,29 +96,51 @@ jobs:
     # The ~200MB browser download is the only slow part (~60s cold, versus ~2s to actually run the harness).  Cached
     # at Playwright's own default location, which is why the profile lets -Djuneau.jsTests.browsers move it out of
     # target/: this workflow's Maven runs use the reactor's "clean verify" defaultGoal, so a cache restored into
-    # target/ would be deleted before it was ever read.  Keyed on the pom that pins the Playwright version rather than
+    # target/ would be deleted before it was ever read.  Keyed on the poms that pin the Playwright version rather than
     # on the literal version, so a bump cannot leave a stale key behind; the restore-keys prefix still gives a
     # partial hit on any other pom edit, and `playwright install` then simply verifies and skips.
     - name: Cache Playwright browsers
       uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
       with:
         path: ~/.cache/ms-playwright
-        key: ${{ runner.os }}-playwright-${{ hashFiles('juneau-rest/juneau-rest-server-views/pom.xml') }}
+        key: ${{ runner.os }}-playwright-${{ hashFiles('juneau-rest/juneau-rest-server-views/pom.xml', 'juneau-petstore/juneau-petstore-core/pom.xml') }}
         restore-keys: ${{ runner.os }}-playwright-
     # Recorded so a future failure log says which Node actually ran, rather than "whatever the runner image had".
     - name: Show Node version
       run: node --version && npm --version
     # -pl/-am rather than -f: -f would resolve the module's SNAPSHOT dependencies from ~/.m2, which a cold runner does
-    # not have.  -pl -am builds the upstream modules but does not re-test them; the matrix job above is what runs
-    # their suites, including the always-on markup/CSS half of this same contract.  This step runs the module's
-    # full *_BrowserTest suite.
+    # not have.  -pl -am builds the upstream modules, and -Dtest then restricts surefire to this module's
+    # *_BrowserTest classes (package-qualified; -Dsurefire.failIfNoSpecifiedTests=false lets the upstream modules,
+    # which have no match, pass), so the upstream suites are not re-run here: the matrix job above runs them,
+    # including the always-on markup/CSS half of this same contract.  The browser tests are the only classes the
+    # js-tests profile switches on, so nothing it enables is left out.
     - name: Run JS harness
       run: >
         mvn --no-transfer-progress --batch-mode --show-version
         -Pjs-tests
         -pl juneau-rest/juneau-rest-server-views -am
         test
+        -Dtest='org.apache.juneau.rest.server.views.*_BrowserTest' -Dsurefire.failIfNoSpecifiedTests=false
         -Djuneau.jsTests.browsers="${HOME}/.cache/ms-playwright"
+    # The petstore console's browser tests: real Jetty on an ephemeral port, the same pinned Playwright and the same
+    # browser cache as the step above.  -pl/-am for the same reason as above; -Dtest is package-qualified because a
+    # bare '*_BrowserTest' would also match the views module's suite, which is in the reactor and enabled by its own
+    # js-tests profile, so it would run twice.
+    - name: js-tests-petstore
+      run: >
+        mvn --no-transfer-progress --batch-mode
+        -Pjs-tests
+        -pl juneau-petstore/juneau-petstore-core -am
+        test
+        -Dtest='org.apache.juneau.petstore.**.*_BrowserTest' -Dsurefire.failIfNoSpecifiedTests=false
+        -Djuneau.jsTests.browsers="${HOME}/.cache/ms-playwright"
+    - name: Upload petstore theme screenshots
+      if: always()
+      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+      with:
+        name: petstore-theme-screenshots
+        path: juneau-petstore/juneau-petstore-core/target/js/snapshots/
+        if-no-files-found: ignore
     # Surefire already prints each failed assertion with the full rendered-state JSON attached, which is the actual
     # diagnosis (a panel reported as "jc-subpanel jc-active" with "visible":false is the blank-tab defect, stated).
     # This step adds what Surefire cannot know: how to reproduce it, and the prober's own stderr for the case where
@@ -143,3 +165,6 @@ jobs:
         cat "${VIEWS}"/target/surefire-reports/*_BrowserTest.txt 2>/dev/null || echo '(none - the failure was before the tests ran)'
         echo '--- Prober stderr (non-empty only if Chromium or Node itself failed) ---'
         cat "${VIEWS}/target/js/prober-stderr.txt" 2>/dev/null || echo '(none)'
+        echo 'Petstore browser tests:'
+        echo '  mvn -Pjs-tests -pl juneau-petstore/juneau-petstore-core -am test -Dtest="org.apache.juneau.petstore.**.*_BrowserTest" -Dsurefire.failIfNoSpecifiedTests=false'
+        echo '  Per-suite stderr: juneau-petstore/juneau-petstore-core/target/js/<suite>-stderr.txt'
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java
index 5d7aaf04b7..4968916b9d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java
@@ -26,7 +26,7 @@ import org.apache.juneau.marshall.json5.*;
  *
  * <h5 class='topic'>Media types</h5>
  * <p>
- * Handles <c>Content-Type</c> types:  <bc>application/json5l, text/json5l</bc>
+ * Handles <c>Content-Type</c> types:  <bc>application/json5l, application/json5lines, text/json5l</bc>
  *
  * <h5 class='topic'>Description</h5>
  * <p>
@@ -80,7 +80,7 @@ public class Json5lParser extends Json5Parser {
 		 * Constructor, default settings.
 		 */
 		protected Builder() {
-			consumes("application/json5l,text/json5l,application/jsonl,application/x-ndjson,text/jsonl")
+			consumes("application/json5l,application/json5lines,text/json5l,application/jsonl,application/jsonlines,application/x-ndjson,text/jsonl")
 				.type(Json5lParser.class);
 		}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java
index cdfdcdf6bc..f319b95ad8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java
@@ -27,7 +27,7 @@ import org.apache.juneau.marshall.jsonl.*;
  *
  * <h5 class='topic'>Media types</h5>
  * <p>
- * Handles <c>Accept</c> types:  <bc>application/json5l, text/json5l</bc>
+ * Handles <c>Accept</c> types:  <bc>application/json5l, application/json5lines, text/json5l</bc>
  * <p>
  * Produces <c>Content-Type</c> types:  <bc>application/json5l</bc>
  *
@@ -92,7 +92,7 @@ public class Json5lSerializer extends JsonlSerializer {
 		 */
 		protected Builder() {
 			produces("application/json5l")
-				.accept("application/json5l,text/json5l,application/jsonl;q=0.9,application/x-ndjson;q=0.9,text/jsonl;q=0.9")
+				.accept("application/json5l,application/json5lines,text/json5l,application/jsonl;q=0.9,application/jsonlines;q=0.9,application/x-ndjson;q=0.9,text/jsonl;q=0.9")
 				.type(Json5lSerializer.class)
 				.useWhitespace(false);
 		}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlParser.java
index d9b9aaca6c..da0f282700 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlParser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlParser.java
@@ -26,7 +26,7 @@ import org.apache.juneau.marshall.json.*;
  *
  * <h5 class='topic'>Media types</h5>
  * <p>
- * Handles <c>Content-Type</c> types:  <bc>application/jsonl, application/x-ndjson, text/jsonl</bc>
+ * Handles <c>Content-Type</c> types:  <bc>application/jsonl, application/jsonlines, application/x-ndjson, text/jsonl</bc>
  *
  * <h5 class='topic'>Description</h5>
  * <p>
@@ -102,7 +102,7 @@ public class JsonlParser extends JsonParser {
 	})
 	public static JsonParser.Builder<?> create() {
 		return JsonParser.create()
-			.consumes("application/jsonl,application/x-ndjson,text/jsonl")
+			.consumes("application/jsonl,application/jsonlines,application/x-ndjson,text/jsonl")
 			.type(JsonlParser.class);
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlSerializer.java
index 687c892204..061ca55f80 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jsonl/JsonlSerializer.java
@@ -24,7 +24,7 @@ import org.apache.juneau.marshall.swap.spi.*;
  *
  * <h5 class='topic'>Media types</h5>
  * <p>
- * Handles <c>Accept</c> types:  <bc>application/jsonl, application/x-ndjson, text/jsonl</bc>
+ * Handles <c>Accept</c> types:  <bc>application/jsonl, application/jsonlines, application/x-ndjson, text/jsonl</bc>
  * <p>
  * Produces <c>Content-Type</c> types:  <bc>application/jsonl</bc>
  *
@@ -112,7 +112,7 @@ public class JsonlSerializer extends JsonSerializer {
 	public static JsonSerializer.Builder<?> create() {
 		return JsonSerializer.create()
 			.produces("application/jsonl")
-			.accept("application/jsonl,application/x-ndjson,text/jsonl")
+			.accept("application/jsonl,application/jsonlines,application/x-ndjson,text/jsonl")
 			.type(JsonlSerializer.class)
 			.useWhitespace(false);
 	}
diff --git a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java
index ae8ad1ace9..9d6c41f59c 100644
--- a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java
+++ b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java
@@ -24,6 +24,8 @@ import java.util.*;
 
 import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.marshall.parser.*;
+import org.apache.juneau.marshall.serializer.*;
 import org.junit.jupiter.api.*;
 
 /**
@@ -70,4 +72,29 @@ class Json5lMediaType_Test {
 		var b = (List<JsonMap>) Json5l.to(json5l, List.class, JsonMap.class);
 		assertBean(b, "0{k}", "{v}");
 	}
+
+	@Test
+	void a05_json5linesAliasAccepted() {
+		var types = new ArrayList<String>();
+		Json5lSerializer.DEFAULT.forEachAcceptMediaType(mt -> types.add(mt.getType() + "/" + mt.getSubType()));
+		assertTrue(types.contains("application/json5lines"), "Expected application/json5lines: " + types);
+		assertTrue(types.contains("application/jsonlines"), "Expected application/jsonlines cross-accept: " + types);
+
+		var consumed = Json5lParser.DEFAULT.getMediaTypes().stream()
+			.map(mt -> mt.getType() + "/" + mt.getSubType())
+			.toList();
+		assertTrue(consumed.contains("application/json5lines"), "Expected application/json5lines: " + consumed);
+		assertTrue(consumed.contains("application/jsonlines"), "Expected application/jsonlines cross-accept: " + consumed);
+	}
+
+	@Test
+	void a06_json5linesAliasResolvesInSets() {
+		var sset = SerializerSet.create().add(Json5lSerializer.class).build();
+		assertTrue(sset.getSerializer("application/json5lines").orElseThrow() instanceof Json5lSerializer);
+		// The produced Content-Type stays canonical.
+		assertEquals("application/json5l", sset.getSerializer("application/json5lines").orElseThrow().getResponseContentType().toString());
+
+		var pset = ParserSet.create().add(Json5lParser.class).build();
+		assertTrue(pset.getParser("application/json5lines").orElseThrow() instanceof Json5lParser);
+	}
 }
diff --git a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/jsonl/JsonlMediaType_Test.java b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/jsonl/JsonlMediaType_Test.java
index 7fbc54d72c..a002d1d61b 100644
--- a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/jsonl/JsonlMediaType_Test.java
+++ b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/jsonl/JsonlMediaType_Test.java
@@ -25,6 +25,8 @@ import java.util.*;
 
 import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.marshall.parser.*;
+import org.apache.juneau.marshall.serializer.*;
 import org.junit.jupiter.api.*;
 
 /**
@@ -68,4 +70,27 @@ class JsonlMediaType_Test {
 		var b = (List<JsonMap>) Jsonl.to(jsonl, List.class, JsonMap.class);
 		assertBean(b, "0{k}", "{v}");
 	}
+
+	@Test
+	void a05_jsonlinesAliasAccepted() {
+		var types = new ArrayList<String>();
+		JsonlSerializer.DEFAULT.forEachAcceptMediaType(mt -> types.add(mt.getType() + "/" + mt.getSubType()));
+		assertTrue(types.contains("application/jsonlines"), "Expected application/jsonlines: " + types);
+
+		var consumed = JsonlParser.DEFAULT.getMediaTypes().stream()
+			.map(mt -> mt.getType() + "/" + mt.getSubType())
+			.toList();
+		assertTrue(consumed.contains("application/jsonlines"), "Expected application/jsonlines: " + consumed);
+	}
+
+	@Test
+	void a06_jsonlinesAliasResolvesInSets() {
+		var sset = SerializerSet.create().add(JsonlSerializer.class).build();
+		assertTrue(sset.getSerializer("application/jsonlines").orElseThrow() instanceof JsonlSerializer);
+		// The produced Content-Type stays canonical.
+		assertEquals("application/jsonl", sset.getSerializer("application/jsonlines").orElseThrow().getResponseContentType().toString());
+
+		var pset = ParserSet.create().add(JsonlParser.class).build();
+		assertTrue(pset.getParser("application/jsonlines").orElseThrow() instanceof JsonlParser);
+	}
 }
diff --git a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/JsonLinesMediaTypeAliases_Test.java b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/JsonLinesMediaTypeAliases_Test.java
new file mode 100644
index 0000000000..840007570f
--- /dev/null
+++ b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/JsonLinesMediaTypeAliases_Test.java
@@ -0,0 +1,110 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.marshall.json5l.*;
+import org.apache.juneau.marshall.jsonl.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.config.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Verifies the {@code application/jsonlines} and {@code application/json5lines} media-type aliases through a
+ * full in-process REST round trip against a {@link BasicUniversalConfig} resource.
+ */
+class JsonLinesMediaTypeAliases_Test extends TestBase {
+
+	public static class Item {
+		public String name;
+		public int age;
+		public Item() {}
+		public Item(String name, int age) { this.name = name; this.age = age; }
+	}
+
+	@Rest(serializers = JsonlSerializer.class, parsers = JsonlParser.class)
+	public static class J extends RestServlet {
+		private static final long serialVersionUID = 1L;
+		@RestGet("/items")
+		public List<Item> items() { return List.of(new Item("a", 1), new Item("b", 2)); }
+
+		@RestPost("/echo")
+		public String echo(@Content List<Item> items) { return items.size() + ":" + items.get(0).name; }
+	}
+
+	@Rest(serializers = Json5lSerializer.class, parsers = Json5lParser.class)
+	public static class K extends RestServlet {
+		private static final long serialVersionUID = 1L;
+		@RestGet("/items")
+		public List<Item> items() { return List.of(new Item("a", 1), new Item("b", 2)); }
+
+		@RestPost("/echo")
+		public String echo(@Content List<Item> items) { return items.size() + ":" + items.get(0).name; }
+	}
+
+	@Rest
+	public static class U extends RestServlet implements BasicUniversalConfig {
+		private static final long serialVersionUID = 1L;
+		@RestGet("/items")
+		public List<Item> items() { return List.of(new Item("a", 1), new Item("b", 2)); }
+	}
+
+	private static final MockRestClient j = MockRestClient.buildLax(J.class);
+	private static final MockRestClient k = MockRestClient.buildLax(K.class);
+	private static final MockRestClient u = MockRestClient.buildLax(U.class);
+
+	@Test void a01_acceptJsonlines_returnsJsonl() throws Exception {
+		var body = j.get("/items").accept("application/jsonlines").run()
+			.assertStatus(200)
+			.assertHeader("Content-Type").is("application/jsonl")
+			.getContent().asString();
+		assertEquals("{\"age\":1,\"name\":\"a\"}\n{\"age\":2,\"name\":\"b\"}", body.trim());
+	}
+
+	@Test void a02_acceptJson5lines_returnsJson5l() throws Exception {
+		var body = k.get("/items").accept("application/json5lines").run()
+			.assertStatus(200)
+			.assertHeader("Content-Type").is("application/json5l")
+			.getContent().asString();
+		assertEquals("{\"age\":1,\"name\":\"a\"}\n{\"age\":2,\"name\":\"b\"}", body.trim());
+	}
+
+	@Test void a03_contentTypeJsonlines_parsed() throws Exception {
+		j.post("/echo", "{\"name\":\"x\",\"age\":1}\n{\"name\":\"y\",\"age\":2}\n")
+			.contentType("application/jsonlines").accept("text/plain").run()
+			.assertStatus(200)
+			.assertContent("2:x");
+	}
+
+	@Test void a04_contentTypeJson5lines_parsed() throws Exception {
+		k.post("/echo", "{name:'x',age:1}\n{name:'y',age:2}\n")
+			.contentType("application/json5lines").accept("text/plain").run()
+			.assertStatus(200)
+			.assertContent("2:x");
+	}
+
+	@Test void a05_universalConfig_acceptsAliases() throws Exception {
+		u.get("/items").accept("application/jsonlines").run().assertStatus(200);
+		u.get("/items").accept("application/json5lines").run().assertStatus(200);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/pom.xml b/juneau-petstore/juneau-petstore-core/pom.xml
index 5bea718bf4..43b3b0d093 100644
--- a/juneau-petstore/juneau-petstore-core/pom.xml
+++ b/juneau-petstore/juneau-petstore-core/pom.xml
@@ -68,7 +68,7 @@
 			<version>2.3.35</version>
 		</dependency>
 
-		<!-- Console UI (C7 Plan A): chrome, FreeMarker bridge, DataTables, views and widgets. -->
+		<!-- Console UI: chrome, FreeMarker bridge, DataTables, views and widgets. -->
 		<dependency>
 			<groupId>org.apache.juneau</groupId>
 			<artifactId>juneau-rest-server-console-ui</artifactId>
@@ -100,6 +100,25 @@
 			<version>${project.version}</version>
 		</dependency>
 
+		<!-- Browser libraries the console's datatables cards bind to; served by VendorRest at /console/vendor/*. -->
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>jquery</artifactId>
+			<version>${webjars-jquery.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.webjars.bowergithub.datatables</groupId>
+			<artifactId>dist-datatables</artifactId>
+			<version>${webjars-datatables.version}</version>
+			<exclusions>
+				<!-- Declared as an open version range; jquery above is the copy that is served. -->
+				<exclusion>
+					<groupId>org.webjars.bowergithub.jquery</groupId>
+					<artifactId>jquery-dist</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
 		<!-- Test dependencies -->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
@@ -125,8 +144,126 @@
 			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
+		<!-- Browser tests: a real Jetty server on an ephemeral port. -->
+		<dependency>
+			<groupId>org.apache.juneau</groupId>
+			<artifactId>juneau-microservice-jetty</artifactId>
+			<version>${project.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.juneau</groupId>
+			<artifactId>juneau-microservice-jetty</artifactId>
+			<version>${project.version}</version>
+			<type>test-jar</type>
+			<scope>test</scope>
+		</dependency>
+		<!-- PetstoreSql_Test: the SQL BeanQuery adapter over H2, compared against the in-memory store. -->
+		<dependency>
+			<groupId>org.apache.juneau</groupId>
+			<artifactId>juneau-beanquery-sql</artifactId>
+			<version>${project.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.juneau</groupId>
+			<artifactId>juneau-beanquery-postgres</artifactId>
+			<version>${project.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.h2database</groupId>
+			<artifactId>h2</artifactId>
+			<version>${h2.version}</version>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 
+	<profiles>
+		<!--
+			Browser tests: the *_BrowserTest classes drive the real console JS in headless Chromium against a live
+			petstore server.  NOT activated by default; a normal build needs no Node, npm, browser or network, and the
+			classes are skipped because no "juneau.jsTests" system property is set:
+
+			    mvn -Pjs-tests -pl juneau-petstore/juneau-petstore-core -am test -Dtest='org.apache.juneau.petstore.**.*_BrowserTest' -Dsurefire.failIfNoSpecifiedTests=false
+
+			A copy of juneau-rest-server-views' js-tests profile (see the notes there on target/js, the browser cache
+			and the -Djuneau.jsTests.* overrides), with only the harness path and the npm packages changed: the
+			petstore pages load jQuery and DataTables from the server (VendorRest, backed by the WebJars).
+		-->
+		<profile>
+			<id>js-tests</id>
+			<properties>
+				<!-- Keep in step with juneau-rest-server-views/pom.xml: one Playwright version, one CI cache. -->
+				<playwright.version>1.49.1</playwright.version>
+				<juneau.jsTests.node>node</juneau.jsTests.node>
+				<juneau.jsTests.npm>npm</juneau.jsTests.npm>
+				<js.tests.dir>${project.build.directory}/js</js.tests.dir>
+				<juneau.jsTests.browsers>${js.tests.dir}/browsers</juneau.jsTests.browsers>
+			</properties>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>3.3.0</version>
+						<executions>
+							<execution>
+								<id>js-install-playwright</id>
+								<phase>process-test-classes</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>${juneau.jsTests.npm}</executable>
+									<arguments>
+										<argument>install</argument>
+										<argument>--no-fund</argument>
+										<argument>--no-audit</argument>
+										<argument>--prefix</argument>
+										<argument>${js.tests.dir}</argument>
+										<argument>playwright@${playwright.version}</argument>
+									</arguments>
+								</configuration>
+							</execution>
+							<execution>
+								<id>js-install-chromium</id>
+								<phase>process-test-classes</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>${juneau.jsTests.node}</executable>
+									<environmentVariables>
+										<PLAYWRIGHT_BROWSERS_PATH>${juneau.jsTests.browsers}</PLAYWRIGHT_BROWSERS_PATH>
+									</environmentVariables>
+									<arguments>
+										<argument>${js.tests.dir}/node_modules/playwright/cli.js</argument>
+										<argument>install</argument>
+										<argument>chromium</argument>
+									</arguments>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-surefire-plugin</artifactId>
+						<configuration>
+							<systemPropertyVariables>
+								<juneau.jsTests>true</juneau.jsTests>
+								<juneau.jsTests.dir>${js.tests.dir}</juneau.jsTests.dir>
+								<juneau.jsTests.harness>${project.basedir}/src/test/js/petstore-browser.cjs</juneau.jsTests.harness>
+								<juneau.jsTests.node>${juneau.jsTests.node}</juneau.jsTests.node>
+								<juneau.jsTests.browsers>${juneau.jsTests.browsers}</juneau.jsTests.browsers>
+							</systemPropertyVariables>
+						</configuration>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
 	<build>
 		<plugins>
 			<plugin>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/AboutRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/AboutRest.java
new file mode 100644
index 0000000000..ac6ce4879b
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/AboutRest.java
@@ -0,0 +1,97 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console;
+
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.beans.*;
+import org.apache.juneau.rest.server.view.*;
+import org.apache.juneau.rest.server.view.freemarker.*;
+
+/**
+ * About: what the petstore demonstrates, as prose cards, plus Juneau's REST utility beans shown against the
+ * {@link Pet} domain bean.
+ *
+ * <p>
+ * {@link BeanDescription}, {@link Hyperlink} and {@link SeeOtherRoot} are small beans meant for REST responses:
+ * {@code OPTIONS}-style introspection, navigational links and root-relative redirects.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jc>// GET /console/about/beans/BeanDescription</jc>
+ * 	<ja>@RestGet</ja>(path=<js>"/beans/BeanDescription"</js>)
+ * 	<jk>public</jk> BeanDescription beanDescription() {
+ * 		<jk>return</jk> BeanDescription.<jsm>of</jsm>(Pet.<jk>class</jk>);
+ * 	}
+ * </p>
+ * <p class='bftl'>
+ * 	&lt;@card id="overview" title="About the petstore"&gt;&lt;div class="jc-prose"&gt;...&lt;/div&gt;&lt;/@card&gt;
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/UtilityBeans">Utility Beans</a>
+ * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
+ * </ul>
+ */
+@Rest(path="/about", title="About")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class AboutRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/** @return The page. */
+	@RestGet(path="/")
+	public View page() {
+		return FreemarkerView.of("about.ftlh");
+	}
+
+	/**
+	 * Lists the three utility-bean demos.
+	 *
+	 * <p>
+	 * Each link is {@code servlet:/}-relative, so the uri carries the {@code beans/} segment.
+	 *
+	 * @return Descriptive links to the demos.
+	 */
+	@RestGet(path="/beans")
+	public ResourceDescriptions beans() {
+		return ResourceDescriptions.create()
+			.append("BeanDescription", "beans/BeanDescription", "Example of a BeanDescription bean, describing the Pet domain bean")
+			.append("Hyperlink", "beans/Hyperlink", "Example of a Hyperlink bean")
+			.append("SeeOtherRoot", "beans/SeeOtherRoot", "Example of a SeeOtherRoot bean");
+	}
+
+	/** @return A {@link BeanDescription} of {@link Pet}. */
+	@RestGet(path="/beans/BeanDescription")
+	public BeanDescription beanDescription() {
+		return BeanDescription.of(Pet.class);
+	}
+
+	/** @return A hyperlink back to this page. */
+	@RestGet(path="/beans/Hyperlink")
+	public Hyperlink hyperlink() {
+		return Hyperlink.create("/console/about", "Back to About");
+	}
+
+	/** @return A redirect to the servlet root. */
+	@RestGet(path="/beans/SeeOtherRoot")
+	public SeeOtherRoot seeOtherRoot() {
+		return SeeOtherRoot.INSTANCE;
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleAssetsRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleAssetsRest.java
new file mode 100644
index 0000000000..3404331a5b
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleAssetsRest.java
@@ -0,0 +1,48 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console;
+
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.console.*;
+import org.apache.juneau.rest.server.servlet.*;
+
+/**
+ * Serves the console chrome assets (the structural {@code chrome.css}, the stock theme packs and
+ * {@code juneau-console.js}) at the context-root {@code /juneau-console/*} URLs that every console page links.
+ *
+ * <p>
+ * The console directive resolves those asset URLs against the context root, not against the page's own mount, so
+ * a host that mounts {@link PetstoreConsoleResource} must also mount this resource as a sibling child for the pages
+ * to be styled and for the console shell to start in a browser.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<ja>@Rest</ja>(children={PetstoreConsoleResource.<jk>class</jk>, ConsoleAssetsRest.<jk>class</jk>})
+ * 	<jk>public class</jk> RootResources <jk>extends</jk> BasicRestServletGroup {}
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * 	<li class='jc'>{@link ConsoleChromeMixin}
+ * </ul>
+ *
+ * @serial exclude
+ */
+@Rest(path="/juneau-console", mixins=ConsoleChromeMixin.class)
+public class ConsoleAssetsRest extends BasicRestServlet {
+
+	private static final long serialVersionUID = 1L;
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleStubs.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleStubs.java
index cc812cb311..d9f615ea63 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleStubs.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleStubs.java
@@ -53,7 +53,8 @@ public final class ConsoleStubs {
 		new Stub("pets/sold", "/console/pets/sold", "Sold pets", "The list of pets that have been sold."),
 		new Stub("pets/changes", "/console/pets/changes", "Pending changes", "Review and apply staged pet edits."),
 		new Stub("orders", "/console/orders", "Orders", "The orders table and order details."),
-		new Stub("users", "/console/users", "Users", "The users table and user details.")
+		new Stub("users", "/console/users", "Users", "The users table and user details."),
+		new Stub("dev/cards", "/console/dev/cards", "Custom cards", "Adopter-defined card types: a JSON-only kpi card and a server-validated inventory gauge.")
 	);
 
 	private ConsoleStubs() {}
@@ -70,4 +71,14 @@ public final class ConsoleStubs {
 			.orElseThrow(() -> new IllegalArgumentException("No console stub for '" + tab + "'"));
 		return FreemarkerView.of("stub.ftlh").attr("tab", s.tab()).attr("title", s.title()).attr("pending", s.pending());
 	}
+
+	/**
+	 * Returns the stub view for a tab path, or empty if there is no such stub.
+	 *
+	 * @param tab The nav id path, for example {@code "dev/flavors/html"}.
+	 * @return The stub view, or empty.
+	 */
+	public static Optional<View> find(String tab) {
+		return ALL.stream().anyMatch(x -> x.tab().equals(tab)) ? Optional.of(view(tab)) : Optional.empty();
+	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleWrites.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleWrites.java
index e36afeca96..48497ad79e 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleWrites.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ConsoleWrites.java
@@ -18,6 +18,7 @@ package org.apache.juneau.petstore.console;
 
 import java.util.*;
 
+import org.apache.juneau.commons.settings.*;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.petstore.auth.*;
 import org.apache.juneau.rest.server.*;
@@ -37,7 +38,7 @@ import org.apache.juneau.rest.server.auth.*;
  */
 public final class ConsoleWrites {
 
-	/** System property that turns on the console write gate. */
+	/** Setting (system property, environment, or any registered source) that turns on the console write gate. */
 	public static final String SECURE_PROPERTY = "petstore.secure";
 
 	private static final StubBearerTokenValidator VALIDATOR = new StubBearerTokenValidator();
@@ -80,11 +81,16 @@ public final class ConsoleWrites {
 	 */
 	public static String actor(RestRequest req) {
 		var user = user(req);
-		if (user == null && Boolean.getBoolean(SECURE_PROPERTY))  // Q:  Can we use Settings here?
+		if (user == null && secure())
 			throw new Unauthorized("Console writes need the demo bearer token").setHeader("WWW-Authenticate", "Bearer realm=\"petstore\"");
 		return user == null ? "console" : "console:" + user;
 	}
 
+	// Resolved on every call, so a changed setting (or a test override) takes effect immediately.
+	private static boolean secure() {
+		return Settings.get().get(SECURE_PROPERTY).asBoolean().orElse(false);
+	}
+
 	/**
 	 * The audit actor reduced to the staging author.
 	 *
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsolePage.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsolePage.java
index 34c32ab132..abb1d34789 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsolePage.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsolePage.java
@@ -19,9 +19,11 @@ package org.apache.juneau.petstore.console;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.petstore.service.*;
 import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.datatables.*;
 import org.apache.juneau.rest.server.servlet.*;
 import org.apache.juneau.rest.server.view.freemarker.*;
 import org.apache.juneau.rest.server.view.freemarker.console.*;
+import org.apache.juneau.rest.server.views.*;
 
 /**
  * Base class for every petstore console page resource: registers the console FreeMarker mixin with
@@ -31,6 +33,10 @@ import org.apache.juneau.rest.server.view.freemarker.console.*;
  * Children don't inherit the parent's mixins, so every page class extends this one instead of re-declaring the
  * mixin, which E-B12 would reject.
  *
+ * <p>
+ * The views and DataTables toolkit packs link their assets page-relative (e.g. {@code /console/store/juneau-views.js}),
+ * so every page also composes {@link ViewsMixin} and {@link DataTablesMixin} to serve them from its own mount.
+ *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
  * 	<ja>@Rest</ja>(path=<js>"/ops/audit"</js>)
@@ -40,7 +46,7 @@ import org.apache.juneau.rest.server.view.freemarker.console.*;
  * 	}
  * </p>
  */
-@Rest(mixins=FreemarkerMixin.class, responseProcessors=FreemarkerViewRenderer.class)
+@Rest(mixins={FreemarkerMixin.class, ViewsMixin.class, DataTablesMixin.class}, responseProcessors=FreemarkerViewRenderer.class)
 public abstract class PetstoreConsolePage extends BasicRestServlet {
 
 	private static final long serialVersionUID = 1L;
@@ -68,7 +74,7 @@ public abstract class PetstoreConsolePage extends BasicRestServlet {
 	 * @return The shared store.
 	 * @throws IllegalStateException If no {@link PetStore} {@code @Bean} was wired into the host.
 	 */
-	protected synchronized PetStore store() {
+	protected PetStore store() {
 		if (store == null)
 			throw new IllegalStateException("No PetStore bean is wired: declare a @Bean PetStore on the host that mounts " + getClass().getSimpleName());
 		return store;
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsoleResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsoleResource.java
index 94f34cc79f..3e7ea7ead2 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsoleResource.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/PetstoreConsoleResource.java
@@ -17,6 +17,7 @@
 package org.apache.juneau.petstore.console;
 
 import org.apache.juneau.http.response.*;
+import org.apache.juneau.petstore.console.dev.*;
 import org.apache.juneau.petstore.console.ops.*;
 import org.apache.juneau.petstore.console.pets.*;
 import org.apache.juneau.rest.server.*;
@@ -51,9 +52,15 @@ import org.apache.juneau.rest.server.view.*;
 	children={
 		StoreRest.class,
 		PetsRest.class,
-		OpsRest.class
+		OpsRest.class,
+		DevRest.class,
+		AboutRest.class,
+		VendorRest.class
 	}
 )
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
 public class PetstoreConsoleResource extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java
index 0698fcb2b2..1c8343e79d 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java
@@ -21,7 +21,7 @@ import org.apache.juneau.rest.server.view.*;
 import org.apache.juneau.rest.server.view.freemarker.*;
 
 /**
- * P1 Store: the petstore dashboard.
+ * Store: the petstore dashboard.
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
@@ -35,12 +35,15 @@ import org.apache.juneau.rest.server.view.freemarker.*;
  * </p>
  */
 @Rest(path="/store", title="Store")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
 public class StoreRest extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
 
 	/**
-	 * Renders P1.
+	 * Renders the Store page.
 	 *
 	 * @return The page view.
 	 */
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/VendorRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/VendorRest.java
new file mode 100644
index 0000000000..582ac44c3a
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/VendorRest.java
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console;
+
+import java.util.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.apache.juneau.rest.server.staticfile.*;
+import org.apache.juneau.commons.inject.*;
+
+/**
+ * Serves the third-party browser libraries the console pages need (jQuery and DataTables) from the WebJars on the
+ * petstore classpath, at {@code /console/vendor/*}.
+ *
+ * <p>
+ * The views toolkit binds datatables cards to jQuery and DataTables but does not bundle them, so the pages that have
+ * such a card load them from here.  The URL path is the WebJar's own layout, for example
+ * {@code /console/vendor/jquery/3.7.1/jquery.min.js} and {@code /console/vendor/datatables.net/js/dataTables.min.js}.
+ */
+@Rest(path="/vendor", staticFiles=VendorRest.WebJars.class)
+public class VendorRest extends BasicRestServlet {
+
+	private static final long serialVersionUID = 1L;
+
+	/** The classpath root every WebJar publishes its files under. */
+	private static final String WEBJARS = "/META-INF/resources/webjars";
+
+	/** The static files backing {@link VendorRest}: only the WebJar root, so nothing else on the classpath is exposed. */
+	public static class WebJars extends BasicStaticFiles {
+
+		/**
+		 * Constructor.
+		 *
+		 * @param beanStore The bean store.
+		 */
+		public WebJars(BeanStore beanStore) {
+			super(StaticFiles.create(beanStore).cp(VendorRest.class, WEBJARS, false).caching(1_000_000).exclude("(?i).*\\.(class|properties)"));
+		}
+	}
+
+	/**
+	 * [GET /vendor/*] &mdash; a file from a WebJar.
+	 *
+	 * @param req The current request.
+	 * @param path The file path within the WebJar root.
+	 * @param locale The request locale.
+	 * @return The file.
+	 */
+	@RestGet(path="/*", summary="WebJar file", swagger=@OpSwagger(ignore=true))
+	public HttpResource get(RestRequest req, @Path("/*") String path, Locale locale) {
+		return StaticFilesMixin.resolveStaticFile(req, path, locale);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/DevRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/DevRest.java
new file mode 100644
index 0000000000..66586dbbb6
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/DevRest.java
@@ -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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import org.apache.juneau.http.response.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.petstore.console.dev.flavors.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.view.*;
+
+/**
+ * The petstore console's Developer section: custom cards, themes, rendering flavors and the secure API demo.
+ *
+ * <p>
+ * Mounted under {@link PetstoreConsoleResource} at {@code /console/dev}.  Each page is its own child resource; routes
+ * whose page has not landed yet answer with a {@link ConsoleStubs} "Coming soon" page so every nav link resolves.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jc>// Mounted as a child of the console root.</jc>
+ * 	<ja>@Rest</ja>(path=<js>"/console"</js>, children={StoreRest.<jk>class</jk>, DevRest.<jk>class</jk>})
+ * 	<jk>public class</jk> PetstoreConsoleResource <jk>extends</jk> PetstoreConsolePage { ... }
+ *
+ * 	<jc>// GET /console/dev  -&gt;  303 to /console/dev/cards</jc>
+ * </p>
+ */
+@Rest(path="/dev", title="Developer", children={FlavorsRest.class, SecureRest.class, ThemesRest.class})
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class DevRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Redirects the section root to its first child.
+	 *
+	 * @param req The request.
+	 * @return A 303 to {@code cards}.
+	 */
+	@RestGet(path="/")
+	public SeeOther index(RestRequest req) {
+		return new SeeOther().setLocation(req.getUriResolver().resolve("servlet:/cards"));
+	}
+
+	/** @return The Custom cards placeholder (see {@link ConsoleStubs}). */
+	@RestGet(path="/cards")
+	public View customCards() { return ConsoleStubs.view("dev/cards"); }
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/SecureApiRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/SecureApiRest.java
new file mode 100644
index 0000000000..bf49ca1d0c
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/SecureApiRest.java
@@ -0,0 +1,105 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import java.security.*;
+import java.util.*;
+
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.response.*;
+import org.apache.juneau.petstore.auth.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.auth.*;
+import org.apache.juneau.rest.server.guard.*;
+
+/**
+ * The bearer-token protected API behind the Secure page.
+ *
+ * <p>
+ * Every op is gated by a fail-closed {@link BearerTokenGuard} backed by {@link StubBearerTokenValidator}: no token
+ * or an unknown token is a {@code 401} with a {@code WWW-Authenticate: Bearer realm="petstore"} challenge.  On
+ * success the resolved {@link Principal} reaches ops as an {@link Auth @Auth} parameter.
+ *
+ * <p>
+ * <b>Why a guard rather than a filter chain?</b>  {@code AuthFilterChain} composes several mechanisms and is
+ * fail-open by design, so a downstream guard decides.  With one mechanism, the op-level guard is the whole story.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jc>// One bean gates every op on the resource.</jc>
+ * 	<ja>@Bean</ja>
+ * 	<jk>public</jk> RestGuardList guards(BeanStore <jv>bs</jv>) {
+ * 		<jk>return</jk> RestGuardList.<jsm>create</jsm>(<jv>bs</jv>)
+ * 			.append(BearerTokenGuard.<jsm>create</jsm>().realm(<js>"petstore"</js>).validator(<jk>new</jk> StubBearerTokenValidator()).build())
+ * 			.build();
+ * 	}
+ *
+ * 	<jc>// curl -H 'Authorization: Bearer petstore-user' http://localhost:10000/console/dev/secure/api/pets</jc>
+ * </p>
+ */
+@Rest(path="/api", title="Secure API")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class SecureApiRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Gates every op on this resource with a bearer-token guard.
+	 *
+	 * @param bs The bean store.
+	 * @return The guard list.
+	 */
+	@Bean
+	public RestGuardList guards(BeanStore bs) {
+		return RestGuardList.create(bs)
+			.append(BearerTokenGuard.create().realm("petstore").validator(new StubBearerTokenValidator()).build())
+			.build();
+	}
+
+	/** @return All pets. */
+	@RestGet(path="/pets")
+	public Collection<Pet> pets() {
+		return store().getPets();
+	}
+
+	/**
+	 * @param id The pet id.
+	 * @return The pet.
+	 * @throws NotFound If there is no such pet.
+	 */
+	@RestGet(path="/pets/{id}")
+	public Pet pet(@Path("id") long id) {
+		var pet = store().getPet(id);
+		if (pet == null)
+			throw new NotFound("Unknown pet '%s'", id);
+		return pet;
+	}
+
+	/**
+	 * @param caller The authenticated principal (never <jk>null</jk>: the guard rejects anonymous calls first).
+	 * @return {@code {name: <principal>}}.
+	 */
+	@RestGet(path="/whoami")
+	public Map<String,String> whoami(@Auth Principal caller) {
+		return Map.of("name", caller.getName());
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/SecureRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/SecureRest.java
new file mode 100644
index 0000000000..20bd018b3b
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/SecureRest.java
@@ -0,0 +1,76 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import static java.nio.charset.StandardCharsets.*;
+
+import java.io.*;
+
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.view.*;
+import org.apache.juneau.rest.server.view.freemarker.*;
+
+/**
+ * Secure: an open console page demonstrating the bearer-token guarded {@link SecureApiRest}.
+ *
+ * <p>
+ * The page's "Try it" card calls {@code /console/dev/secure/api/whoami} from the browser with no token, the demo
+ * token or a wrong token, and shows the status and body inline.  The demo tokens come from
+ * {@code StubBearerTokenValidator}: {@code petstore-user} (alice) and {@code petstore-admin} (admin).
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bftl'>
+ * 	&lt;@page tab="dev/secure" init=["/console/dev/secure/secure.js"]&gt;
+ * 	&lt;@card id="try" title="Try it"&gt;
+ * 	&lt;button type="button" data-secure-call="petstore-user"&gt;With the demo token&lt;/button&gt;
+ * 	&lt;output id="secure-result"&gt;&lt;/output&gt;
+ * 	&lt;/@card&gt;
+ * 	&lt;/@page&gt;
+ * </p>
+ */
+@Rest(path="/secure", title="Secure", children={SecureApiRest.class})
+@SuppressWarnings({
+	"java:S110", // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+	"resource" // The response writer is owned by the servlet container; Eclipse JDT @Owning warning is by design.
+})
+public class SecureRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/** @return The page. */
+	@RestGet(path="/")
+	public View page() {
+		return FreemarkerView.of("secure.ftlh");
+	}
+
+	/**
+	 * Serves the page-local script.
+	 *
+	 * @param res The response.
+	 * @throws IOException If the bundled script can't be read.
+	 */
+	@RestGet(path="/secure.js")
+	public void script(RestResponse res) throws IOException {
+		try (var in = SecureRest.class.getResourceAsStream("secure.js")) {
+			if (in == null)
+				throw new IllegalStateException("Missing classpath resource 'secure.js' beside " + SecureRest.class.getName());
+			res.setContentType("text/javascript;charset=utf-8");
+			res.getWriter().write(new String(in.readAllBytes(), UTF_8));
+		}
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/ThemesRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/ThemesRest.java
new file mode 100644
index 0000000000..c2a6b8c00b
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/ThemesRest.java
@@ -0,0 +1,91 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import java.util.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.response.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.console.*;
+import org.apache.juneau.rest.server.view.*;
+import org.apache.juneau.rest.server.view.freemarker.*;
+
+/**
+ * Themes: the stock console themes and one custom token theme, switchable with {@code ?theme=}.
+ *
+ * <p>
+ * The chosen name reaches {@code base.ftlh} as the {@code petstoreTheme} view attribute, which feeds
+ * {@code <@console theme=...>}.  {@code ?theme=custom} also sets {@code petstoreCustomTheme}, which turns on a
+ * {@code <@theme name="light-brown">} block with two {@code <@token>} overrides: a leaf and an alias.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bftl'>
+ * 	&lt;@console brand="My app" theme="light-brown"&gt;
+ * 	&lt;@theme name="light-brown"&gt;
+ * 	&lt;@token name="--jc-pill-red-bg" value="#fdeceb"/&gt;
+ * 	&lt;@token name="--jc-tab-bar-bg" alias="var(--jc-card-bg)"/&gt;
+ * 	&lt;/@theme&gt;
+ * 	...
+ * 	&lt;/@console&gt;
+ * </p>
+ * <p class='bcode'>
+ * 	GET /console/dev/themes?theme=gray
+ * </p>
+ */
+@Rest(path="/themes", title="Themes")
+@SuppressWarnings({
+	"java:S110", // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+	"java:S2386" // CHOICES is an immutable List.copyOf(); public so templates and tests can read it.
+})
+public class ThemesRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/** The name {@code ?theme=} takes for the custom token theme. */
+	public static final String CUSTOM = "custom";
+
+	/** The custom theme's stock seed. */
+	public static final String CUSTOM_SEED = "light-brown";
+
+	/** Every name {@code ?theme=} accepts, stock themes first. */
+	public static final List<String> CHOICES = choices();
+
+	private static List<String> choices() {
+		var l = new ArrayList<>(ConsoleChromeMixin.BUILTIN_THEME_NAMES);
+		l.add(CUSTOM);
+		return List.copyOf(l);
+	}
+
+	/**
+	 * @param theme The theme to render in, or <jk>null</jk> for the default.
+	 * @return The page.
+	 * @throws BadRequest If the name is not one of {@link #CHOICES}.
+	 */
+	@RestGet(path="/")
+	public View page(@Query("theme") String theme) {
+		var name = theme == null || theme.isEmpty() ? ConsoleChromeMixin.BUILTIN_THEME_NAMES.get(0) : theme;
+		if (! CHOICES.contains(name))
+			throw new BadRequest("Unknown theme '%s'; choose one of: '%s'", name, String.join(", ", CHOICES));
+		var custom = CUSTOM.equals(name);
+		return FreemarkerView.of("themes.ftlh")
+			.attr("petstoreTheme", custom ? CUSTOM_SEED : name)
+			.attr("petstoreCustomTheme", custom)
+			.attr("themeChoices", CHOICES);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/Flavor.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/Flavor.java
new file mode 100644
index 0000000000..9bc2328f95
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/Flavor.java
@@ -0,0 +1,93 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev.flavors;
+
+import java.util.*;
+
+import org.apache.juneau.http.response.*;
+import org.apache.juneau.rest.server.view.*;
+import org.apache.juneau.rest.server.view.freemarker.*;
+
+/**
+ * The four petstore rendering flavors shown under Developer &gt; Rendering flavors.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jc>// The console page for one flavor.</jc>
+ * 	View <jv>page</jv> = Flavor.<jsm>of</jsm>(<js>"mustache"</js>).page();
+ * </p>
+ */
+public enum Flavor {
+
+	/** HTML5 bean builder, serialized by {@code HtmlSerializer}. */
+	HTML("html", "Html", "The pet list built in Java with the HTML5 bean builder (div, table, tr, td) and serialized as a bare fragment.", "console/dev/flavors/HtmlFlavorRest.java"),
+
+	/** FreeMarker view rendering. */
+	FREEMARKER("freemarker", "Freemarker", "The pet list rendered from a FreeMarker template through the FreeMarker view bridge.", "console/dev/flavors/FreemarkerFlavorRest.java"),
+
+	/** Mustache view rendering. */
+	MUSTACHE("mustache", "Mustache", "The pet list rendered from a Mustache template through the Mustache view bridge.", "console/dev/flavors/MustacheFlavorRest.java"),
+
+	/** The standalone React app at /petstore-ui. */
+	REACT("react", "React", "The standalone React single-page app, served at /petstore-ui and embedded here unchanged.", "rest/PetstoreUiResource.java");
+
+	private static final String SOURCE_ROOT =
+		"https://github.com/apache/juneau/blob/master/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/";
+
+	private final String id;
+	private final String label;
+	private final String caption;
+	private final String sourceFile;
+
+	Flavor(String id, String label, String caption, String sourceFile) {
+		this.id = id;
+		this.label = label;
+		this.caption = caption;
+		this.sourceFile = sourceFile;
+	}
+
+	/**
+	 * Looks up a flavor by its URL id.
+	 *
+	 * @param id The id, for example {@code "html"}.
+	 * @return The flavor.
+	 * @throws NotFound If the id names no flavor.
+	 */
+	public static Flavor of(String id) {
+		return Arrays.stream(values()).filter(x -> x.id.equals(id)).findFirst()
+			.orElseThrow(() -> new NotFound("Unknown rendering flavor '%s'", id));
+	}
+
+	/** @return The URL id. */
+	public String id() { return id; }
+
+	/** @return The caption shown above the rendered output. */
+	public String caption() { return caption; }
+
+	/** @return The GitHub URL of the class that renders this flavor. */
+	public String sourceUrl() { return SOURCE_ROOT + sourceFile; }
+
+	/** @return The console page for this flavor. */
+	public View page() {
+		// React embeds the standalone app; the server flavors load their fragment with src=.
+		return FreemarkerView.of(this == REACT ? "flavors-react.ftlh" : "flavors.ftlh")
+			.attr("engine", id)
+			.attr("label", label)
+			.attr("caption", caption)
+			.attr("source", sourceUrl());
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FlavorPets.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FlavorPets.java
new file mode 100644
index 0000000000..2ab31f6997
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FlavorPets.java
@@ -0,0 +1,66 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev.flavors;
+
+import java.util.*;
+
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.petstore.service.*;
+
+/**
+ * The pet rows every rendering flavor shows: the first {@value #LIMIT} pets by id, with display-ready strings.
+ *
+ * <p>
+ * Formatting happens here, once, so the three engines differ only in markup and the flavor tests can compare their
+ * cells exactly.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	List&lt;Map&lt;String,String&gt;&gt; <jv>rows</jv> = FlavorPets.<jsm>rows</jsm>(<jv>store</jv>);
+ * 	<jv>rows</jv>.get(0).get(<js>"name"</js>);   <jc>// "Mr. Frisky"</jc>
+ * </p>
+ */
+public final class FlavorPets {
+
+	/** How many pets each flavor shows. */
+	public static final int LIMIT = 10;
+
+	private FlavorPets() {}
+
+	/**
+	 * Returns the display rows.
+	 *
+	 * @param store The store.  Must not be <jk>null</jk>.
+	 * @return Rows with keys {@code name}, {@code species}, {@code price} (two decimals) and {@code status}.
+	 */
+	public static List<Map<String,String>> rows(PetStore store) {
+		return store.getPets().stream()
+			.sorted(Comparator.comparingLong(Pet::getId))
+			.limit(LIMIT)
+			.map(FlavorPets::row)
+			.toList();
+	}
+
+	private static Map<String,String> row(Pet p) {
+		var m = new LinkedHashMap<String,String>();
+		m.put("name", p.getName());
+		m.put("species", String.valueOf(p.getSpecies()));
+		m.put("price", String.format(Locale.ROOT, "%.2f", p.getPrice()));
+		m.put("status", String.valueOf(p.getStatus()));
+		return m;
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FlavorsRest.java
similarity index 51%
copy from juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java
copy to juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FlavorsRest.java
index 03848e8281..668d8755fb 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FlavorsRest.java
@@ -14,40 +14,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.petstore.console.ops;
+package org.apache.juneau.petstore.console.dev.flavors;
 
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.petstore.console.*;
 import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.view.*;
 
 /**
- * Operations: parent of the Jobs and Audit pages.  {@code GET /console/ops} redirects to Jobs.
+ * Rendering flavors: the same pet list rendered by each of the petstore's view technologies, inside the console.
  *
- * <h5 class='section'>Example:</h5>
- * <p class='bjava'>
- * 	<ja>@Rest</ja>(path=<js>"/ops"</js>, children={JobsRest.<jk>class</jk>})
- * 	<jk>public class</jk> OpsRest <jk>extends</jk> PetstoreConsolePage {
+ * <p>
+ * Html, Freemarker and Mustache are child resources, each serving its page at {@code /} and a chrome-less fragment
+ * at {@code /fragment} that the page's {@code html} card loads with {@code src=}.  React is the standalone app at
+ * {@code /petstore-ui}, embedded in an iframe.
  *
- * 		<ja>@RestGet</ja>(path=<js>"/"</js>)
- * 		<jk>public</jk> SeeOther index(RestRequest <jv>req</jv>) {
- * 			<jk>return new</jk> SeeOther().setLocation(<jv>req</jv>.getUriResolver().resolve(<js>"servlet:/jobs"</js>));
- * 		}
- * 	}
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * 	&lt;@card id="flavor" title="Pets" src="/console/dev/flavors/mustache/fragment"/&gt;
  * </p>
  */
-@Rest(path="/ops", title="Operations", children={JobsRest.class, AuditRest.class})
-public class OpsRest extends PetstoreConsolePage {
+@Rest(
+	path="/flavors",
+	title="Rendering flavors",
+	children={HtmlFlavorRest.class, FreemarkerFlavorRest.class, MustacheFlavorRest.class}
+)
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class FlavorsRest extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
 
 	/**
-	 * {@code GET /console/ops} redirects to the Jobs page.
+	 * Redirects to the first flavor.
 	 *
 	 * @param req The request.
-	 * @return A 303 to {@code /console/ops/jobs}.
+	 * @return A 303 to {@code html}.
 	 */
 	@RestGet(path="/")
 	public SeeOther index(RestRequest req) {
-		return new SeeOther().setLocation(req.getUriResolver().resolve("servlet:/jobs"));
+		return new SeeOther().setLocation(req.getUriResolver().resolve("servlet:/html"));
+	}
+
+	/** @return The React flavor page. */
+	@RestGet(path="/react")
+	public View react() {
+		return Flavor.REACT.page();
 	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FreemarkerFlavorRest.java
similarity index 54%
copy from juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java
copy to juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FreemarkerFlavorRest.java
index 0698fcb2b2..b6ea8d8a81 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/StoreRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/FreemarkerFlavorRest.java
@@ -14,38 +14,39 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.petstore.console;
+package org.apache.juneau.petstore.console.dev.flavors;
 
+import org.apache.juneau.petstore.console.*;
 import org.apache.juneau.rest.server.*;
 import org.apache.juneau.rest.server.view.*;
 import org.apache.juneau.rest.server.view.freemarker.*;
 
 /**
- * P1 Store: the petstore dashboard.
+ * The Freemarker rendering flavor: the pet list rendered from {@code fragments/pets.ftlh}.
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
- * 	<ja>@RestGet</ja>(path=<js>"/"</js>)
- * 	<jk>public</jk> View page() { <jk>return</jk> FreemarkerView.<jsm>of</jsm>(<js>"store.ftlh"</js>); }
- * </p>
- * <p class='bcode'>
- * 	&lt;@page tab="store" toolkit="views"&gt;
- * 	  &lt;@card id="welcome" title="Welcome to the petstore"&gt;&lt;div class="jc-prose"&gt;...&lt;/div&gt;&lt;/@card&gt;
- * 	&lt;/@page&gt;
+ * 	<jc>// A FreemarkerView with no &lt;@page&gt; renders as a plain fragment, even through the console mixin.</jc>
+ * 	<jk>return</jk> FreemarkerView.<jsm>of</jsm>(<js>"fragments/pets.ftlh"</js>).attr(<js>"pets"</js>, FlavorPets.<jsm>rows</jsm>(store()));
  * </p>
  */
-@Rest(path="/store", title="Store")
-public class StoreRest extends PetstoreConsolePage {
+@Rest(path="/freemarker", title="Freemarker flavor")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class FreemarkerFlavorRest extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
 
-	/**
-	 * Renders P1.
-	 *
-	 * @return The page view.
-	 */
+	/** @return The console page. */
 	@RestGet(path="/")
 	public View page() {
-		return FreemarkerView.of("store.ftlh");
+		return Flavor.FREEMARKER.page();
+	}
+
+	/** @return The pet list fragment. */
+	@RestGet(path="/fragment")
+	public View fragment() {
+		return FreemarkerView.of("fragments/pets.ftlh").attr("pets", FlavorPets.rows(store()));
 	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/HtmlFlavorRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/HtmlFlavorRest.java
new file mode 100644
index 0000000000..eb995a8329
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/HtmlFlavorRest.java
@@ -0,0 +1,61 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev.flavors;
+
+import static org.apache.juneau.bean.html5.HtmlBuilder.*;
+
+import java.util.*;
+
+import org.apache.juneau.bean.html5.*;
+import org.apache.juneau.marshall.html.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.view.*;
+
+/**
+ * The Html rendering flavor: the pet list built with the HTML5 bean builder.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jc>// GET /console/dev/flavors/html/fragment returns a bare table, no page chrome:</jc>
+ * 	<jc>// &lt;table class="petstore-flavor" data-flavor="html"&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;...</jc>
+ * </p>
+ */
+@Rest(path="/html", title="Html flavor")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class HtmlFlavorRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/** @return The console page. */
+	@RestGet(path="/")
+	public View page() {
+		return Flavor.HTML.page();
+	}
+
+	/** @return The pet list as a bare HTML table. */
+	@RestGet(path="/fragment", serializers=HtmlSerializer.class)
+	public Table fragment() {
+		var rows = new ArrayList<Object>();
+		rows.add(tr(th("Name"), th("Species"), th("Price"), th("Status")));
+		for (var r : FlavorPets.rows(store()))
+			rows.add(tr(td(r.get("name")), td(r.get("species")), td(r.get("price")), td(r.get("status"))));
+		return table(rows.toArray()).class_("petstore-flavor").attr("data-flavor", "html");
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/MustacheFlavorRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/MustacheFlavorRest.java
new file mode 100644
index 0000000000..95821f2586
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/MustacheFlavorRest.java
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev.flavors;
+
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.view.*;
+import org.apache.juneau.rest.server.view.mustache.*;
+
+/**
+ * The Mustache rendering flavor: the pet list rendered from {@code console/fragments/pets.mustache}.
+ *
+ * <p>
+ * The page itself is a console page (FreeMarker); only the fragment is Mustache, so this resource adds the Mustache
+ * renderer beside the inherited FreeMarker one.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jk>return</jk> MustacheView.<jsm>of</jsm>(<js>"pets"</js>).attr(<js>"pets"</js>, FlavorPets.<jsm>rows</jsm>(store()));
+ * </p>
+ */
+@Rest(
+	path="/mustache",
+	title="Mustache flavor",
+	responseProcessors={MustacheViewRenderer.class}
+)
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
+public class MustacheFlavorRest extends PetstoreConsolePage {
+
+	private static final long serialVersionUID = 1L;
+
+	/** Where the Mustache fragment lives. */
+	public static final String FRAGMENTS = "/org/apache/juneau/petstore/console/fragments/";
+
+	/** @return The Mustache engine for the fragment. */
+	@Bean
+	public MustacheMixin mustache() {
+		return MustacheMixin.create().basePath(FRAGMENTS).templateSuffix(".mustache").build();
+	}
+
+	/** @return The console page. */
+	@RestGet(path="/")
+	public View page() {
+		return Flavor.MUSTACHE.page();
+	}
+
+	/** @return The pet list fragment. */
+	@RestGet(path="/fragment")
+	public View fragment() {
+		return MustacheView.of("pets").attr("pets", FlavorPets.rows(store()));
+	}
+}
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/package-info.java
similarity index 62%
copy from juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
copy to juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/package-info.java
index 059865e006..2bfd1ee498 100644
--- a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/flavors/package-info.java
@@ -15,13 +15,11 @@
  * limitations under the License.
  */
 /**
- * Test helpers for console pages: {@link org.apache.juneau.rest.server.console.test.PageContractAssert} asserts on the
- * {@code #juneau-page} contract of a rendered page instead of on rendered chrome HTML.
+ * Petstore console Rendering flavors: one console page per petstore view technology.
  *
- * <p>
- * Main scope on purpose (C1-D3): adopters already depend on {@code juneau-rest-server-console-ui}. The package has no
- * test-framework dependency; failures are plain {@link java.lang.AssertionError}s.
- *
- * @since 10.0.0
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	View <jv>page</jv> = Flavor.<jsm>of</jsm>(<js>"freemarker"</js>).page();
+ * </p>
  */
-package org.apache.juneau.rest.server.console.test;
+package org.apache.juneau.petstore.console.dev.flavors;
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/package-info.java
similarity index 62%
copy from juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
copy to juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/package-info.java
index 059865e006..12cb37e9a4 100644
--- a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/dev/package-info.java
@@ -15,13 +15,12 @@
  * limitations under the License.
  */
 /**
- * Test helpers for console pages: {@link org.apache.juneau.rest.server.console.test.PageContractAssert} asserts on the
- * {@code #juneau-page} contract of a rendered page instead of on rendered chrome HTML.
+ * Petstore console Developer pages: custom cards, themes, rendering flavors and the secure API demo.
  *
- * <p>
- * Main scope on purpose (C1-D3): adopters already depend on {@code juneau-rest-server-console-ui}. The package has no
- * test-framework dependency; failures are plain {@link java.lang.AssertionError}s.
- *
- * @since 10.0.0
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<ja>@Rest</ja>(path=<js>"/dev"</js>, children={FlavorsRest.<jk>class</jk>})
+ * 	<jk>public class</jk> DevRest <jk>extends</jk> PetstoreConsolePage {}
+ * </p>
  */
-package org.apache.juneau.rest.server.console.test;
+package org.apache.juneau.petstore.console.dev;
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/AuditRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/AuditRest.java
index f5898a551a..7ca7e1d479 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/AuditRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/AuditRest.java
@@ -29,7 +29,7 @@ import org.apache.juneau.rest.server.view.*;
 import org.apache.juneau.rest.server.view.freemarker.*;
 
 /**
- * P7 Audit log: a read-only, server-mode table over the store's audit trail.
+ * Audit log: a read-only, server-mode table over the store's audit trail.
  *
  * <p>
  * The table's {@code at} column takes a timestamp range as a column search value; the query layer parses the ISO-8601
@@ -49,6 +49,9 @@ import org.apache.juneau.rest.server.view.freemarker.*;
  * </p>
  */
 @Rest(path="/audit", title="Audit")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
 public class AuditRest extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
@@ -56,7 +59,7 @@ public class AuditRest extends PetstoreConsolePage {
 	private static final int ROWS_CAP = 200;
 
 	/**
-	 * Renders P7.
+	 * Renders the Audit page.
 	 *
 	 * @return The page view.
 	 */
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/JobsRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/JobsRest.java
index 71c4fcfdfb..da456a68bb 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/JobsRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/JobsRest.java
@@ -34,7 +34,7 @@ import org.apache.juneau.rest.server.views.*;
 import org.apache.juneau.rest.server.widgets.*;
 
 /**
- * P6 Jobs: the console's async-job reference (spec R11/R12).  A restock submits once per idempotency key, answers
+ * Jobs: the console's async-job reference.  A restock submits once per idempotency key, answers
  * 202 with an {@link AsyncJobRef}, and the views runtime follows its {@code streamUrl} until the job settles.
  *
  * <p>
@@ -54,6 +54,12 @@ import org.apache.juneau.rest.server.widgets.*;
  * </p>
  */
 @Rest(path="/jobs", title="Jobs")
+@SuppressWarnings({
+	"java:S110", // Inheritance depth comes from the BasicRestServlet hierarchy, not this demo page.
+	"java:S1192", // Duplicated literals are wire keys of the job rows; constants would obscure the schema.
+	"java:S2654", // The demo intentionally runs restocks on its own pool and serializes the idempotency check.
+	"resource" // The registry and pool are owned by this resource and closed in closeJobs(); Eclipse JDT @Owning warning is by design.
+})
 public class JobsRest extends PetstoreConsolePage implements AsyncJobsMixin {
 
 	private static final long serialVersionUID = 1L;
@@ -69,7 +75,7 @@ public class JobsRest extends PetstoreConsolePage implements AsyncJobsMixin {
 		t.setDaemon(true);
 		return t;
 	});
-	/** Submitted jobs, newest first, for the P6 table. */
+	/** Submitted jobs, newest first, for the Jobs table. */
 	private final transient Deque<JsonMap> submitted = new ConcurrentLinkedDeque<>();
 
 	/** How many submitted jobs the table remembers. */
@@ -123,7 +129,16 @@ public class JobsRest extends PetstoreConsolePage implements AsyncJobsMixin {
 	public AsyncJobRegistry asyncJobRegistry() { return jobs; }
 
 	/**
-	 * Renders P6.
+	 * Stops the restock pool and the registry's timeout scheduler when the resource is destroyed.
+	 */
+	@RestDestroy
+	public void closeJobs() {
+		pool.shutdownNow();
+		jobs.close();
+	}
+
+	/**
+	 * Renders the Jobs page.
 	 *
 	 * @return The page view.
 	 */
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java
index 03848e8281..f2c2250c5f 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/ops/OpsRest.java
@@ -36,6 +36,9 @@ import org.apache.juneau.rest.server.*;
  * </p>
  */
 @Rest(path="/ops", title="Operations", children={JobsRest.class, AuditRest.class})
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
 public class OpsRest extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/pets/PetsRest.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/pets/PetsRest.java
index 608917557c..cc9f3e081f 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/pets/PetsRest.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/pets/PetsRest.java
@@ -35,7 +35,7 @@ import org.apache.juneau.rest.server.views.*;
 
 /**
  * The pets JSON endpoints (server-mode datatable query over BeanQuery, create/replace/delete, sell, stage, and the
- * per-pet feeds).  The P2 Pets, P3 Sold and P10 Pending changes pages are still "Coming soon" placeholders
+ * per-pet feeds).  The Pets, Sold and Pending changes pages are still "Coming soon" placeholders
  * ({@link ConsoleStubs}).
  *
  * <h5 class='section'>Example:</h5>
@@ -54,12 +54,15 @@ import org.apache.juneau.rest.server.views.*;
  * 	&lt;/@card&gt;
  * </p>
  * <p class='bjava'>
- * 	<jc>// The same table in the C3 builder twin.</jc>
+ * 	<jc>// The same table in the builder twin.</jc>
  * 	TableSpec.<jsm>create</jsm>(<js>"pets"</js>).dataMode(DataMode.<jsf>SERVER</jsf>).dataUrl(<js>"/console/pets/query"</js>)
  * 		.columns(Column.<jsm>create</jsm>(<js>"name"</js>).label(<js>"Name"</js>));
  * </p>
  */
 @Rest(path="/pets", title="Pets")
+@SuppressWarnings({
+	"java:S110" // Inheritance depth comes from the BasicRestServlet hierarchy, not this page.
+})
 public class PetsRest extends PetstoreConsolePage {
 
 	private static final long serialVersionUID = 1L;
@@ -126,14 +129,7 @@ public class PetsRest extends PetstoreConsolePage {
 		var old = require(id);
 		validate(pet);
 		pet.setId(id);
-		if (pet.getStatus() == null)  // Let's use defaultX() methods here like we use on DAOs in IRS.
-			pet.setStatus(old.getStatus());
-		if (pet.getSpecies() == null)
-			pet.setSpecies(old.getSpecies());
-		if (pet.getTags() == null)
-			pet.setTags(old.getTags());
-		if (pet.getPhoto() == null)
-			pet.setPhoto(old.getPhoto());
+		pet.defaultStatus(old.getStatus()).defaultSpecies(old.getSpecies()).defaultTags(old.getTags()).defaultPhoto(old.getPhoto());
 		return ActionResult.success(store().updatePet(pet, actor)).message(f("Saved '%s'", pet.getName()));
 	}
 
@@ -154,7 +150,7 @@ public class PetsRest extends PetstoreConsolePage {
 	}
 
 	/**
-	 * Marks a pet sold.  The P2 bulk "Mark sold (N)" action calls this per row.
+	 * Marks a pet sold.  The Pets page bulk "Mark sold (N)" action calls this per row.
 	 *
 	 * @param req The request.
 	 * @param id The pet id.
@@ -175,11 +171,11 @@ public class PetsRest extends PetstoreConsolePage {
 	}
 
 	/**
-	 * Stages an inline edit (R11) for review on P10.  The pet itself is not changed until the change is applied.
+	 * Stages an inline edit for review on the Pending changes page.  The pet itself is not changed until the change is applied.
 	 *
 	 * <p>
 	 * {@link PendingChanges#stage} owns which fields are stageable and its messages.  The photo is deliberately not
-	 * stageable: the P4 Photo tab edits it directly through the API's own {@code PUT /petstore/pets/{id}/photo}.
+	 * stageable: the pet detail Photo tab edits it directly through the API's own {@code PUT /petstore/pets/{id}/photo}.
 	 *
 	 * @param req The request.
 	 * @param id The pet id.
@@ -203,7 +199,7 @@ public class PetsRest extends PetstoreConsolePage {
 
 	/**
 	 * @param id The pet id.
-	 * @return This pet's orders (the R12 related list), by id.
+	 * @return This pet's orders (the related list), by id.
 	 * @throws NotFound If the pet does not exist.
 	 */
 	@RestGet(path="/{id}/orders/rows")
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/dto/Pet.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/dto/Pet.java
index 9f81749b7c..3c8b858f13 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/dto/Pet.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/dto/Pet.java
@@ -179,4 +179,52 @@ public class Pet {
 		photo = value;
 		return this;
 	}
+
+	/**
+	 * Sets the species only if it is currently <jk>null</jk>.
+	 *
+	 * @param value The fallback value.
+	 * @return This object.
+	 */
+	public Pet defaultSpecies(Species value) {
+		if (species == null)
+			species = value;
+		return this;
+	}
+
+	/**
+	 * Sets the tags only if they are currently <jk>null</jk>.
+	 *
+	 * @param value The fallback value.
+	 * @return This object.
+	 */
+	public Pet defaultTags(List<String> value) {
+		if (tags == null)
+			tags = cp(value);
+		return this;
+	}
+
+	/**
+	 * Sets the lifecycle status only if it is currently <jk>null</jk>.
+	 *
+	 * @param value The fallback value.
+	 * @return This object.
+	 */
+	public Pet defaultStatus(PetStatus value) {
+		if (status == null)
+			status = value;
+		return this;
+	}
+
+	/**
+	 * Sets the photo URI only if it is currently <jk>null</jk>.
+	 *
+	 * @param value The fallback value.
+	 * @return This object.
+	 */
+	public Pet defaultPhoto(String value) {
+		if (photo == null)
+			photo = value;
+		return this;
+	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetFreemarkerViewResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetFreemarkerViewResource.java
deleted file mode 100644
index d80b5fa656..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetFreemarkerViewResource.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.http.response.*;
-import org.apache.juneau.petstore.service.*;
-import org.apache.juneau.rest.server.*;
-import org.apache.juneau.rest.server.servlet.*;
-import org.apache.juneau.rest.server.view.*;
-import org.apache.juneau.rest.server.view.freemarker.*;
-
-/**
- * Petstore FreeMarker view demo.
- *
- * <p>
- * Pure-classpath analogue of {@link PetMustacheViewResource}: the FreeMarker bridge is engine-agnostic and
- * renders identically under both the Jetty and Spring Boot deployments.  Resolves
- * {@code petstore-templates/pet.ftlh} via the configured {@link FreemarkerMixin}.
- *
- * <h5 class='section'>See Also:</h5><ul>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/FreemarkerViewSupport">FreeMarker View Support</a>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
- * </ul>
- */
-@Rest(
-	path="/pet-views/freemarker",
-	title="Pet detail (FreeMarker)",
-	description="Renders a pet detail page through the FreeMarker view-rendering bridge.",
-	responseProcessors={FreemarkerViewRenderer.class}
-)
-@SuppressWarnings({
-	"java:S110" // Inheritance depth acceptable for example/demo code
-})
-public class PetFreemarkerViewResource extends BasicRestServlet {
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * The shared store, injected from the parent's bean store (the runner registers one seeded {@code PetStore}).
-	 * When this resource is mounted standalone (unit tests), {@link #store()} falls back to a classic-rows store.
-	 */
-	@SuppressWarnings({
-		"java:S2226" // the field is set by the framework's @Bean injection after construction, so it cannot be final.
-	})
-	@Bean
-	private transient PetStore store;
-
-	@SuppressWarnings({
-		"java:S2654" // synchronized guards the lazy standalone fallback, racing against the injected value.
-	})
-	private synchronized PetStore store() {
-		if (store == null)
-			store = new PetStore();
-		return store;
-	}
-
-	/**
-	 * Provides a {@link FreemarkerMixin} configured to resolve templates under {@code petstore-templates/}.
-	 *
-	 * @return The freemarker mixin bean.
-	 */
-	@Bean
-	public FreemarkerMixin freemarker() {
-		return FreemarkerMixin.create()
-			.basePath("/petstore-templates/")
-			.build();
-	}
-
-	/**
-	 * GET /pets/{id}/view — renders a pet detail HTML page through FreeMarker.
-	 *
-	 * @param id The pet ID.
-	 * @return A {@link FreemarkerView} bound to the {@code pet.ftlh} template.
-	 * @throws NotFound If no pet with the given ID exists.
-	 */
-	@RestGet("/pets/{id}/view")
-	public View viewPet(@Path("id") long id) {
-		var pet = store().getPet(id);
-		if (pet == null)
-			throw new NotFound("Pet not found: id=%s", id);
-		return FreemarkerView.of("pet.ftlh").attr("pet", pet);
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java
deleted file mode 100644
index abbeb9dc87..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import static org.apache.juneau.bean.html5.HtmlBuilder.*;
-
-import org.apache.juneau.bean.html5.*;
-import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.commons.utils.Shorts;
-import org.apache.juneau.http.*;
-import org.apache.juneau.http.response.*;
-import org.apache.juneau.petstore.service.*;
-import org.apache.juneau.rest.server.*;
-import org.apache.juneau.rest.server.beans.*;
-import org.apache.juneau.rest.server.servlet.*;
-
-/**
- * Petstore-flavored demo of Juneau's HTML5 bean-builder DSL.
- *
- * <p>
- * Ports the deleted {@code juneau-examples-rest} {@code HtmlBeansResource} demo, swapping its generic
- * div/form/table samples for hand-built HTML5 fragments rendered from live petstore data — a "pet card"
- * {@link Div} and an all-pets {@link Table} — using {@link org.apache.juneau.bean.html5.HtmlBuilder} static
- * factory methods instead of relying on the framework's automatic HTML-doc view.
- *
- * <h5 class='section'>See Also:</h5><ul>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlBeans">Using with HTML Beans</a>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
- * </ul>
- */
-@Rest(
-	path="/petstore-html",
-	title="Petstore HTML5 bean builder",
-	description="Hand-built HTML5 div/table fragments rendered from live petstore data."
-)
-@SuppressWarnings({
-	"java:S110" // Inheritance depth acceptable for example/demo code
-})
-public class PetHtmlResource extends BasicRestServlet {
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * The shared store, injected from the parent's bean store (the runner registers one seeded {@code PetStore}).
-	 * When this resource is mounted standalone (unit tests), {@link #store()} falls back to a classic-rows store.
-	 */
-	@SuppressWarnings({
-		"java:S2226" // the field is set by the framework's @Bean injection after construction, so it cannot be final.
-	})
-	@Bean
-	private transient PetStore store;
-
-	@SuppressWarnings({
-		"java:S2654" // synchronized guards the lazy standalone fallback, racing against the injected value.
-	})
-	private synchronized PetStore store() {
-		if (store == null)
-			store = new PetStore();
-		return store;
-	}
-
-	/**
-	 * Lists the child endpoints.
-	 *
-	 * @return Descriptive links to the child endpoints.
-	 */
-	@RestGet("/")
-	public ResourceDescriptions getChildDescriptions() {
-		return ResourceDescriptions
-			.create()
-			.append("table", "All pets, rendered as a hand-built HTML5 table")
-			.append("card/1", "A single pet, rendered as a hand-built HTML5 'pet card' div");
-	}
-
-	/**
-	 * Renders a single pet as a hand-built HTML5 "pet card" div.
-	 *
-	 * @param id The pet ID.
-	 * @return A div containing the pet's name, species, price, and status.
-	 * @throws NotFound If no pet with the given ID exists.
-	 */
-	@RestGet("/card/{id}")
-	public Div getPetCard(@Path("id") long id) {
-		var pet = store().getPet(id);
-		if (pet == null)
-			throw new NotFound("Pet not found: id=%s", id);
-		return div(
-			p(b(pet.getName())),
-			p("Species: " + pet.getSpecies()),
-			p("Price: $" + pet.getPrice()),
-			p("Status: " + pet.getStatus())
-		);
-	}
-
-	/**
-	 * Renders all pets as a hand-built HTML5 table.
-	 *
-	 * @return A table with one row per pet (name, species, price, status).
-	 */
-	@RestGet("/table")
-	public Table getPetTable() {
-		var rows = Shorts.l();
-		rows.add(tr(th("Name"), th("Species"), th("Price"), th("Status")));
-		for (var pet : store().getPets())
-			rows.add(tr(td(pet.getName()), td(pet.getSpecies()), td(pet.getPrice()), td(pet.getStatus())));
-		return table(rows.toArray());
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetInfoResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetInfoResource.java
deleted file mode 100644
index 078a2a5dba..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetInfoResource.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import org.apache.juneau.petstore.dto.*;
-import org.apache.juneau.rest.server.*;
-import org.apache.juneau.rest.server.beans.*;
-import org.apache.juneau.rest.server.servlet.*;
-
-/**
- * Petstore-flavored demo of Juneau's REST-server utility beans.
- *
- * <p>
- * Ports the deleted {@code juneau-examples-rest} {@code UtilityBeansResource} demo, swapping its generic
- * {@code Address} sample bean for the petstore domain's {@link Pet} bean.  Shows {@link BeanDescription},
- * {@link Hyperlink}, and {@link SeeOtherRoot} — three small utility beans intended for use in REST responses
- * (e.g. {@code OPTIONS} introspection, navigational links, and root-relative redirects).
- *
- * <h5 class='section'>See Also:</h5><ul>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/UtilityBeans">Utility Beans</a>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
- * </ul>
- */
-@Rest(
-	path="/petstore-info",
-	title="Petstore utility beans",
-	description="Demonstrates BeanDescription/Hyperlink/SeeOtherRoot against the Pet domain bean."
-)
-@SuppressWarnings({
-	"java:S110" // Inheritance depth acceptable for example/demo code
-})
-public class PetInfoResource extends BasicRestServlet {
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Lists the child endpoints.
-	 *
-	 * @return Descriptive links to the child endpoints.
-	 */
-	@RestGet("/")
-	public ResourceDescriptions getChildDescriptions() {
-		return ResourceDescriptions
-			.create()
-			.append("BeanDescription", "Example of a BeanDescription bean, describing the Pet domain bean")
-			.append("Hyperlink", "Example of a Hyperlink bean")
-			.append("SeeOtherRoot", "Example of a SeeOtherRoot bean");
-	}
-
-	/**
-	 * Describes the petstore {@link Pet} bean's properties.
-	 *
-	 * @return A {@link BeanDescription} of {@link Pet}.
-	 */
-	@RestGet("/BeanDescription")
-	public BeanDescription getPetBeanDescription() {
-		return BeanDescription.of(Pet.class);
-	}
-
-	/**
-	 * Returns a hyperlink back to this resource's root.
-	 *
-	 * @return A hyperlink pointing at {@code /petstore-info}.
-	 */
-	@RestGet("/Hyperlink")
-	public Hyperlink getHyperlink() {
-		return Hyperlink.create("/petstore-info", "Back to /petstore-info");
-	}
-
-	/**
-	 * Redirects to the servlet root.
-	 *
-	 * @return A redirect to the servlet root.
-	 */
-	@RestGet("/SeeOtherRoot")
-	public SeeOtherRoot getSeeOtherRoot() {
-		return SeeOtherRoot.INSTANCE;
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetMustacheViewResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetMustacheViewResource.java
deleted file mode 100644
index 468fa09aa2..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetMustacheViewResource.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.http.response.*;
-import org.apache.juneau.petstore.service.*;
-import org.apache.juneau.rest.server.*;
-import org.apache.juneau.rest.server.servlet.*;
-import org.apache.juneau.rest.server.view.*;
-import org.apache.juneau.rest.server.view.mustache.*;
-
-/**
- * Petstore Mustache view demo.
- *
- * <p>
- * Demonstrates rendering a templated HTML pet detail page through the {@code juneau-rest-server-view-mustache}
- * bridge.  Lives in {@code juneau-petstore-core} so both the Jetty and Spring Boot deployments inherit it
- * unchanged — Mustache is a pure-classpath engine and renders identically under either embedded container.
- *
- * <p>
- * The {@code @RestGet("/pets/{id}/view")} method returns {@link MustacheView}; the configured
- * {@link MustacheViewRenderer} (registered via {@code @Rest(responseProcessors=...)}) detects the return type,
- * resolves the {@code pet.mustache} template under {@code petstore-templates/} on the classpath, and renders
- * it directly to the response writer.
- *
- * <h5 class='section'>See Also:</h5><ul>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MustacheViewSupport">Mustache View Support</a>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
- * </ul>
- */
-@Rest(
-	path="/pet-views/mustache",
-	title="Pet detail (Mustache)",
-	description="Renders a pet detail page through the Mustache view-rendering bridge.",
-	responseProcessors={MustacheViewRenderer.class}
-)
-@SuppressWarnings({
-	"java:S110" // Inheritance depth acceptable for example/demo code
-})
-public class PetMustacheViewResource extends BasicRestServlet {
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * The shared store, injected from the parent's bean store (the runner registers one seeded {@code PetStore}).
-	 * When this resource is mounted standalone (unit tests), {@link #store()} falls back to a classic-rows store.
-	 */
-	@SuppressWarnings({
-		"java:S2226" // the field is set by the framework's @Bean injection after construction, so it cannot be final.
-	})
-	@Bean
-	private transient PetStore store;
-
-	@SuppressWarnings({
-		"java:S2654" // synchronized guards the lazy standalone fallback, racing against the injected value.
-	})
-	private synchronized PetStore store() {
-		if (store == null)
-			store = new PetStore();
-		return store;
-	}
-
-	/**
-	 * Provides a {@link MustacheMixin} configured to resolve templates under {@code petstore-templates/} with
-	 * an implicit {@code .mustache} suffix — so {@code MustacheView.of("pet")} resolves to the classpath
-	 * resource {@code petstore-templates/pet.mustache}.
-	 *
-	 * @return The mustache mixin bean.
-	 */
-	@Bean
-	public MustacheMixin mustache() {
-		return MustacheMixin.create()
-			.basePath("/petstore-templates/")
-			.templateSuffix(".mustache")
-			.build();
-	}
-
-	/**
-	 * GET /pets/{id}/view — renders a pet detail HTML page through Mustache.
-	 *
-	 * @param id The pet ID.
-	 * @return A {@link MustacheView} bound to the {@code pet} template.
-	 * @throws NotFound If no pet with the given ID exists.
-	 */
-	@RestGet("/pets/{id}/view")
-	public View viewPet(@Path("id") long id) {
-		var pet = store().getPet(id);
-		if (pet == null)
-			throw new NotFound("Pet not found: id=%s", id);
-		return MustacheView.of("pet").attr("pet", pet);
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetSecureResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetSecureResource.java
deleted file mode 100644
index 2b0b26b0fb..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetSecureResource.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import java.security.*;
-import java.util.*;
-
-import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.http.response.*;
-import org.apache.juneau.petstore.auth.*;
-import org.apache.juneau.petstore.dto.*;
-import org.apache.juneau.petstore.service.*;
-import org.apache.juneau.rest.server.*;
-import org.apache.juneau.rest.server.auth.*;
-import org.apache.juneau.rest.server.guard.*;
-import org.apache.juneau.rest.server.servlet.*;
-
-/**
- * Secure analogue of {@link PetStoreResource} demonstrating Juneau's {@link BearerTokenGuard} +
- * {@link StubBearerTokenValidator} pattern.
- *
- * <p>
- * Mounted at {@code /petstore-secure/*} and gated by a {@link BearerTokenGuard} that delegates to a
- * {@link StubBearerTokenValidator}.  Any request lacking a valid {@code Authorization: Bearer <token>} header
- * receives {@code 401} with a {@code WWW-Authenticate: Bearer realm="petstore"} challenge.  On success, the
- * resolved {@link Principal} is stashed on the request and surfaces through {@link Auth @Auth Principal} op
- * parameters — see {@link #whoami(Principal)}.
- *
- * <p>
- * <b>Why guard rather than chain?</b>  Juneau also ships an {@code AuthFilterChain} for composing multiple
- * mechanisms (JWT bearer, API key, OAuth, SAML, OIDC).  The chain is fail-open by design — a request that
- * matches the chain's path pattern but presents no recognised credentials is passed through unchanged so a
- * downstream guard can decide.  For a single-mechanism demo, the op-level {@code BearerTokenGuard} (fail-closed
- * by default) is the right surface.  In a real multi-mechanism deployment you'd compose
- * {@code AuthFilterChain.create(...).append(filterA, "/secure/*").append(filterB, "/secure/*").build()} and
- * mount it via {@code @Bean AuthFilterChain} (auto-mounted by {@code JettyServerComponent}) or a Spring
- * {@code FilterRegistrationBean}, then add this guard for fail-closed enforcement.
- *
- * <p>
- * The CRUD surface is intentionally a thin subset of {@link PetStoreResource} — the demo is the auth gate, not
- * the surface.  If you need richer auth integration (role-based guards, multi-mechanism chains, server-side
- * OAuth2 / SAML / OIDC-RP), consult the {@code juneau-rest-server-auth-*} modules.
- *
- * <h5 class='section'>See Also:</h5><ul>
- * 	<li class='jc'>{@link BearerTokenGuard}
- * 	<li class='jc'>{@link AuthFilterChain}
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerAuthGuards">AuthN Guards</a>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/AuthFilterFramework">AuthN Filter Framework</a>
- * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
- * </ul>
- */
-@Rest(
-	path="/petstore-secure",
-	title="Petstore (secure)",
-	description="Auth-gated subset of the petstore CRUD surface; bearer-token-protected via AuthFilterChain."
-)
-@SuppressWarnings({
-	"java:S110" // Inheritance depth acceptable for example/demo code
-})
-public class PetSecureResource extends BasicRestServlet {
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * The shared store, injected from the parent's bean store (the runner registers one seeded {@code PetStore}).
-	 * When this resource is mounted standalone (unit tests), {@link #store()} falls back to a classic-rows store.
-	 */
-	@SuppressWarnings({
-		"java:S2226" // the field is set by the framework's @Bean injection after construction, so it cannot be final.
-	})
-	@Bean
-	private transient PetStore store;
-
-	@SuppressWarnings({
-		"java:S2654" // synchronized guards the lazy standalone fallback, racing against the injected value.
-	})
-	private synchronized PetStore store() {
-		if (store == null)
-			store = new PetStore();
-		return store;
-	}
-
-	/**
-	 * Provides the {@link RestGuardList} that gates every op on this resource with a
-	 * {@link BearerTokenGuard} — fail-closed bearer-token enforcement.
-	 *
-	 * @param bs The bean store (Juneau supplies this automatically).
-	 * @return The guard list applied to all ops on this resource.
-	 */
-	@Bean
-	public RestGuardList guards(BeanStore bs) {
-		return RestGuardList.create(bs)
-			.append(
-				BearerTokenGuard.create()
-					.realm("petstore")
-					.validator(new StubBearerTokenValidator())
-					.build())
-			.build();
-	}
-
-	/**
-	 * Lists all pets.  Accessible only with a valid bearer token.
-	 *
-	 * @return All pets.
-	 */
-	@RestGet("/pets")
-	public Collection<Pet> getPets() {
-		return store().getPets();
-	}
-
-	/**
-	 * Retrieves a pet by ID.  Accessible only with a valid bearer token.
-	 *
-	 * @param id The pet ID.
-	 * @return The pet.
-	 * @throws NotFound If no pet with the given ID exists.
-	 */
-	@RestGet("/pets/{id}")
-	public Pet getPet(@Path("id") long id) {
-		var pet = store().getPet(id);
-		if (pet == null)
-			throw new NotFound("Pet not found: id=%s", id);
-		return pet;
-	}
-
-	/**
-	 * Returns the name of the authenticated principal — the simplest possible "auth worked" probe.
-	 *
-	 * @param caller The principal stashed on the request by the {@code AuthFilterChain}.  Never {@code null} —
-	 * 	if no caller is authenticated the chain rejects with {@code 401} before this op runs.
-	 * @return A small map carrying the principal's display name.
-	 */
-	@RestGet("/whoami")
-	public Map<String,String> whoami(@Auth Principal caller) {
-		return Map.of("name", caller.getName());
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetstoreUiResource.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetstoreUiResource.java
index 34cc55e30b..a8619effb7 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetstoreUiResource.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetstoreUiResource.java
@@ -16,6 +16,9 @@
  */
 package org.apache.juneau.petstore.rest;
 
+import java.util.*;
+
+import org.apache.juneau.http.*;
 import org.apache.juneau.rest.server.*;
 import org.apache.juneau.rest.server.servlet.*;
 import org.apache.juneau.rest.server.staticfile.*;
@@ -30,6 +33,10 @@ import org.apache.juneau.rest.server.staticfile.*;
  * decoupled-SPA pattern alongside the server-rendered HTML-doc UI.
  *
  * <p>
+ * The app is also served at the resource root ({@code GET /petstore-ui}), so the console can embed and link it without
+ * knowing the static mount.
+ *
+ * <p>
  * Lives in {@code juneau-petstore-core} so both the Jetty and Spring Boot deployments inherit it for free.
  *
  * <h5 class='section'>See Also:</h5><ul>
@@ -48,4 +55,16 @@ import org.apache.juneau.rest.server.staticfile.*;
 public class PetstoreUiResource extends BasicRestServlet {
 
 	private static final long serialVersionUID = 1L;
+
+	/**
+	 * [GET /] &mdash; the single-page app.
+	 *
+	 * @param req The current request.
+	 * @param locale The request locale.
+	 * @return The app's HTML page.
+	 */
+	@RestGet(path="/", summary="The React app", swagger=@OpSwagger(ignore=true))
+	public HttpResource app(RestRequest req, Locale locale) {
+		return StaticFilesMixin.resolveStaticFile(req, "petstore-ui.html", locale);
+	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/AuditEntry.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/AuditEntry.java
index 70888e6ac7..f148cfc5ab 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/AuditEntry.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/AuditEntry.java
@@ -24,7 +24,7 @@ import java.time.*;
  * <p>
  * {@link PetStore} appends one entry for every successful create, update or delete, whether it came from the
  * {@code /petstore} API (actor {@code "api"}) or from the console (actor {@code "console:<user>"}).  Failed mutations
- * append nothing.  The P7 Audit console page lists these entries.
+ * append nothing.  The Audit console page lists these entries.
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChange.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChange.java
index 8b5f91171e..2f21207f02 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChange.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChange.java
@@ -17,7 +17,7 @@
 package org.apache.juneau.petstore.service;
 
 /**
- * One staged inline edit of a pet field, waiting on the P10 Pending changes page to be applied or discarded.
+ * One staged inline edit of a pet field, waiting on the Pending changes page to be applied or discarded.
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChanges.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChanges.java
index 5f1b6bf60e..dbdaa34859 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChanges.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PendingChanges.java
@@ -25,8 +25,8 @@ import java.util.function.*;
 import org.apache.juneau.petstore.dto.*;
 
 /**
- * The staging area for P4 inline edits (R11): edits are staged here instead of mutating the pet, then applied
- * (perRow, with retry on failure) or discarded (aggregate) from the P10 Pending changes page.
+ * The staging area for pet-detail inline edits: edits are staged here instead of mutating the pet, then applied
+ * (perRow, with retry on failure) or discarded (aggregate) from the Pending changes page.
  *
  * <p>
  * Obtained from {@link PetStore#pendingChanges()}.  All methods are synchronized; the store is a demo, not a
@@ -44,7 +44,7 @@ import org.apache.juneau.petstore.dto.*;
 public class PendingChanges {
 
 	/**
-	 * Result of an aggregate discard, mapped 1:1 onto C2's {@code BulkResult} by {@code ChangesRest}.
+	 * Result of an aggregate discard, mapped 1:1 onto the console's {@code BulkResult} by {@code ChangesRest}.
 	 *
 	 * @param succeeded Discarded change ids.
 	 * @param notFound Ids that name no change (including unparseable ids).
diff --git a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PetStore.java b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PetStore.java
index cbda8b863f..1fae289a33 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PetStore.java
+++ b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/service/PetStore.java
@@ -531,7 +531,7 @@ public class PetStore {
 	 * Appends an audit entry stamped with this store's clock.
 	 *
 	 * <p>
-	 * Public so console operations that are not plain CRUD (P6 restock, P10 apply/discard) are audited too.
+	 * Public so console operations that are not plain CRUD (restock, apply/discard pending changes) are audited too.
 	 *
 	 * @param actor Who did it.
 	 * @param entity The entity kind.
@@ -551,7 +551,7 @@ public class PetStore {
 	}
 
 	/**
-	 * Returns the staging area for console inline edits (P4 -> P10).
+	 * Returns the staging area for console inline edits (pet detail to Pending changes).
 	 *
 	 * @return The pending-changes staging area.  Never <jk>null</jk>.
 	 */
@@ -590,7 +590,7 @@ public class PetStore {
 	}
 
 	//------------------------------------------------------------------------------------------------------------------
-	// BeanQuery views (D-P5)
+	// BeanQuery views
 	//------------------------------------------------------------------------------------------------------------------
 
 	/**
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/dev/secure.js b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/dev/secure.js
new file mode 100644
index 0000000000..b24a367321
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/dev/secure.js
@@ -0,0 +1,43 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Secure "Try it" card: each [data-secure-call] button calls the guarded whoami endpoint with no token
+ * ("none"), or with the bearer token named by the attribute, and prints the status and body in #secure-result.
+ */
+(function () {
+	'use strict';
+	document.addEventListener('click', async function (e) {
+		var b = e.target.closest('[data-secure-call]');
+		if (!b) return;
+		var token = b.getAttribute('data-secure-call');
+		var out = document.getElementById('secure-result');
+		var headers = { 'Accept': 'application/json' };
+		if (token !== 'none') headers['Authorization'] = 'Bearer ' + token;
+		out.removeAttribute('data-status');
+		out.textContent = 'Calling...';
+		try {
+			var r = await fetch(b.getAttribute('data-secure-url'), { credentials: 'same-origin', headers: headers });
+			var text = await r.text();
+			out.textContent = r.status + ' ' + (r.ok ? 'OK' : r.statusText) + '\n' + text;
+			out.setAttribute('data-status', String(r.status));
+		} catch (err) {
+			out.textContent = 'Request failed: ' + err;
+			out.setAttribute('data-status', 'error');
+		}
+	});
+})();
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/fragments/pets.mustache
similarity index 62%
copy from juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache
copy to juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/fragments/pets.mustache
index e7dc523f33..4fc0d76a47 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/fragments/pets.mustache
@@ -13,20 +13,9 @@
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
-}}<!DOCTYPE html>
-<html>
-<head>
-	<title>Pet: {{pet.name}}</title>
-	<style>body { font-family: sans-serif; padding: 2em; } dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.5em 1em; }</style>
-</head>
-<body>
-	<h1>{{pet.name}}</h1>
-	<dl>
-		<dt>ID</dt><dd>{{pet.id}}</dd>
-		<dt>Species</dt><dd>{{pet.species}}</dd>
-		<dt>Price</dt><dd>{{pet.price}}</dd>
-		<dt>Status</dt><dd>{{pet.status}}</dd>
-	</dl>
-	<p><em>Rendered via Mustache.</em></p>
-</body>
-</html>
+}}<table class="petstore-flavor" data-flavor="mustache">
+<tr><th>Name</th><th>Species</th><th>Price</th><th>Status</th></tr>
+{{#pets}}
+<tr><td>{{name}}</td><td>{{species}}</td><td>{{price}}</td><td>{{status}}</td></tr>
+{{/pets}}
+</table>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/about.ftlh b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/about.ftlh
new file mode 100644
index 0000000000..3fa9e427b8
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/about.ftlh
@@ -0,0 +1,44 @@
+<#--
+  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
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--><@page tab="about">
+<@card id="overview" title="About the petstore">
+<div class="jc-prose">
+<p>The petstore is Apache Juneau's sample application and its console regression suite. Every page is built from
+the same console pieces your own app can use: navigation, cards, data tables over BeanQuery, async jobs, themes
+and a bearer-token guard.</p>
+<p>The data is seeded deterministically, so the same pets, orders and users appear on every start.</p>
+</div>
+</...@card>
+<@card id="links" title="Elsewhere">
+<div class="jc-prose">
+<ul>
+<li><a href="/petstore/api">The REST API</a>, with its generated Swagger page.</li>
+<li><a href="/petstore-ui">The React app</a>, a standalone single-page client of the same API.</li>
+<li><a href="https://juneau.apache.org/docs/topics/JuneauPetstore">The petstore docs</a>.</li>
+</ul>
+</div>
+</...@card>
+<@card id="beans" title="REST utility beans">
+<div class="jc-prose">
+<p>Three small beans for REST responses, shown against the <code>Pet</code> bean:</p>
+<ul>
+<li><a href="/console/about/beans/BeanDescription">BeanDescription</a>: the properties of a bean class.</li>
+<li><a href="/console/about/beans/Hyperlink">Hyperlink</a>: a navigational link.</li>
+<li><a href="/console/about/beans/SeeOtherRoot">SeeOtherRoot</a>: a redirect to the servlet root.</li>
+</ul>
+</div>
+</...@card>
+</...@page>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/base.ftlh b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/base.ftlh
index 793d1c913c..dc28a878b4 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/base.ftlh
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/base.ftlh
@@ -13,7 +13,16 @@
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
---><@console brand="Juneau Petstore">
+--><@console brand="Juneau Petstore" theme=(petstoreTheme!"open")>
+<#if petstoreCustomTheme!false>
+<@theme name="light-brown">
+<@token name="--jc-pill-red-bg" value="#fdeceb"/>
+<@token name="--jc-tab-bar-bg" alias="var(--jc-card-bg)"/>
+</...@theme>
+</#if>
+<#-- The views runtime binds datatables cards to jQuery and DataTables, which Juneau does not bundle; this app serves them from its WebJars. -->
+<@scripts phase="after-toolkit"><script src="/console/vendor/jquery/3.7.1/jquery.min.js"></script>
+<script src="/console/vendor/datatables.net/js/dataTables.min.js"></s...@scripts>
 <@navigation>
 <@node id="store" label="Store" href="/console/store"/>
 <@node id="pets" label="Pets" href="/console/pets">
@@ -26,6 +35,18 @@
 <@node id="jobs" label="Jobs" href="/console/ops/jobs"/>
 <@node id="audit" label="Audit" href="/console/ops/audit"/>
 </...@node>
+<@node id="dev" label="Developer" href="/console/dev/cards">
+<@node id="cards" label="Custom cards" href="/console/dev/cards"/>
+<@node id="themes" label="Themes" href="/console/dev/themes"/>
+<@node id="flavors" label="Rendering flavors" href="/console/dev/flavors/html">
+<@node id="html" label="Html" href="/console/dev/flavors/html"/>
+<@node id="freemarker" label="Freemarker" href="/console/dev/flavors/freemarker"/>
+<@node id="mustache" label="Mustache" href="/console/dev/flavors/mustache"/>
+<@node id="react" label="React" href="/console/dev/flavors/react"/>
+</...@node>
+<@node id="secure" label="Secure" href="/console/dev/secure"/>
+</...@node>
+<@node id="about" label="About" href="/console/about"/>
 </...@navigation>
 <@main/>
 <@footer text="Apache Juneau petstore sample"/>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.ftlh b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/flavors-react.ftlh
similarity index 57%
rename from juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.ftlh
rename to juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/flavors-react.ftlh
index 0912ec9ae2..028c75a070 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.ftlh
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/flavors-react.ftlh
@@ -6,28 +6,18 @@
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at
 
-      http://www.apache.org/licenses/LICENSE-2.0
+       http://www.apache.org/licenses/LICENSE-2.0
 
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
--->
-<!DOCTYPE html>
-<html>
-<head>
-	<title>Pet: ${pet.name}</title>
-	<style>body { font-family: sans-serif; padding: 2em; } dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.5em 1em; }</style>
-</head>
-<body>
-	<h1>${pet.name}</h1>
-	<dl>
-		<dt>ID</dt><dd>${pet.id}</dd>
-		<dt>Species</dt><dd>${pet.species}</dd>
-		<dt>Price</dt><dd>${pet.price?string("0.00")}</dd>
-		<dt>Status</dt><dd>${pet.status}</dd>
-	</dl>
-	<p><em>Rendered via FreeMarker.</em></p>
-</body>
-</html>
+--><@page tab="dev/flavors/react" toolkit="views">
+<@card id="caption" title=label>
+<div class="jc-prose"><p>${caption}</p><p><a href="${source}">View source</a></p></div>
+</...@card>
+<@card id="flavor" title="React app">
+<iframe src="/petstore-ui" title="Petstore React app" class="petstore-flavor-frame" width="100%" height="640"></iframe>
+</...@card>
+</...@page>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/flavors.ftlh
similarity index 58%
copy from juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache
copy to juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/flavors.ftlh
index e7dc523f33..0688d89d45 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/flavors.ftlh
@@ -1,4 +1,4 @@
-{{!
+<#--
   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.
@@ -6,27 +6,16 @@
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at
 
-      http://www.apache.org/licenses/LICENSE-2.0
+       http://www.apache.org/licenses/LICENSE-2.0
 
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
-}}<!DOCTYPE html>
-<html>
-<head>
-	<title>Pet: {{pet.name}}</title>
-	<style>body { font-family: sans-serif; padding: 2em; } dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.5em 1em; }</style>
-</head>
-<body>
-	<h1>{{pet.name}}</h1>
-	<dl>
-		<dt>ID</dt><dd>{{pet.id}}</dd>
-		<dt>Species</dt><dd>{{pet.species}}</dd>
-		<dt>Price</dt><dd>{{pet.price}}</dd>
-		<dt>Status</dt><dd>{{pet.status}}</dd>
-	</dl>
-	<p><em>Rendered via Mustache.</em></p>
-</body>
-</html>
+--><@page tab="dev/flavors/${engine}" toolkit="views">
+<@card id="caption" title=label>
+<div class="jc-prose"><p>${caption}</p><p><a href="${source}">View source</a></p></div>
+</...@card>
+<@card id="flavor" title="Pets" src="/console/dev/flavors/${engine}/fragment"/>
+</...@page>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/fragments/pets.ftlh
similarity index 58%
rename from juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache
rename to juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/fragments/pets.ftlh
index e7dc523f33..e15363cceb 100644
--- a/juneau-petstore/juneau-petstore-core/src/main/resources/petstore-templates/pet.mustache
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/fragments/pets.ftlh
@@ -1,4 +1,4 @@
-{{!
+<#--
   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.
@@ -6,27 +6,17 @@
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at
 
-      http://www.apache.org/licenses/LICENSE-2.0
+       http://www.apache.org/licenses/LICENSE-2.0
 
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
-}}<!DOCTYPE html>
-<html>
-<head>
-	<title>Pet: {{pet.name}}</title>
-	<style>body { font-family: sans-serif; padding: 2em; } dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.5em 1em; }</style>
-</head>
-<body>
-	<h1>{{pet.name}}</h1>
-	<dl>
-		<dt>ID</dt><dd>{{pet.id}}</dd>
-		<dt>Species</dt><dd>{{pet.species}}</dd>
-		<dt>Price</dt><dd>{{pet.price}}</dd>
-		<dt>Status</dt><dd>{{pet.status}}</dd>
-	</dl>
-	<p><em>Rendered via Mustache.</em></p>
-</body>
-</html>
+--><#-- Chrome-less FreeMarker fragment for the Rendering flavors page.  No <@page>: an html card loads it with src=. -->
+<table class="petstore-flavor" data-flavor="freemarker">
+<tr><th>Name</th><th>Species</th><th>Price</th><th>Status</th></tr>
+<#list pets as p>
+<tr><td>${p.name}</td><td>${p.species}</td><td>${p.price}</td><td>${p.status}</td></tr>
+</#list>
+</table>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/secure.ftlh b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/secure.ftlh
new file mode 100644
index 0000000000..cc2eaae5cd
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/secure.ftlh
@@ -0,0 +1,35 @@
+<#--
+  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
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--><@page tab="dev/secure" init=["/console/dev/secure/secure.js"]>
+<@card id="about" title="Bearer-token guard">
+<div class="jc-prose">
+<p>This page is open. The API under <code>/console/dev/secure/api</code> is not: every call needs an
+<code>Authorization: Bearer &lt;token&gt;</code> header, checked by a fail-closed guard.</p>
+<p>Demo tokens: <code>petstore-user</code> (alice) and <code>petstore-admin</code> (admin). Anything else is a 401.</p>
+<pre><code>curl -H 'Authorization: Bearer petstore-user' http://localhost:10000/console/dev/secure/api/pets</code></pre>
+</div>
+</...@card>
+<@card id="try" title="Try it">
+<div class="jc-prose">
+<p>
+<button type="button" data-secure-call="none" data-secure-url="/console/dev/secure/api/whoami">Without a token</button>
+<button type="button" data-secure-call="petstore-user" data-secure-url="/console/dev/secure/api/whoami">With the demo token</button>
+<button type="button" data-secure-call="wrong-token" data-secure-url="/console/dev/secure/api/whoami">With a wrong token</button>
+</p>
+<pre><output id="secure-result" aria-live="polite"></output></pre>
+</div>
+</...@card>
+</...@page>
diff --git a/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/themes.ftlh b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/themes.ftlh
new file mode 100644
index 0000000000..e88de35fa0
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/main/resources/org/apache/juneau/petstore/console/templates/themes.ftlh
@@ -0,0 +1,44 @@
+<#--
+  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
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--><@page tab="dev/themes">
+<@card id="swatches" title="This theme">
+<div class="jc-prose">
+<p>The page is drawn in <strong>${petstoreTheme!"open"}</strong><#if petstoreCustomTheme!false>, with two custom tokens</#if>.</p>
+<p>
+<span class="tag stage released">Available</span>
+<span class="tag stage pending">Pending</span>
+<span class="tag stage failed">Sold</span>
+</p>
+</div>
+</...@card>
+<@card id="picker" title="Pick a theme">
+<div class="jc-prose">
+<ul>
+<#list themeChoices as t><li><a href="/console/dev/themes?theme=${t}">${t}</a></li></#list>
+</ul>
+</div>
+</...@card>
+<@card id="authoring" title="Writing a custom theme">
+<div class="jc-prose">
+<p>Seed from a stock theme and override tokens. A <code>value</code> sets a token; an <code>alias</code> makes it
+follow another token.</p>
+<pre><code>&lt;@theme name="light-brown"&gt;
+&lt;@token name="--jc-pill-red-bg" value="#fdeceb"/&gt;
+&lt;@token name="--jc-tab-bar-bg" alias="var(--jc-card-bg)"/&gt;
+&lt;/@theme&gt;</code></pre>
+</div>
+</...@card>
+</...@page>
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/PetstoreSharedStore_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/PetstoreSharedStore_Test.java
index d5e9dff105..9b6c7f857f 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/PetstoreSharedStore_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/PetstoreSharedStore_Test.java
@@ -20,6 +20,8 @@ import static org.apache.juneau.test.bct.BctAssertions.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.petstore.console.*;
 import org.apache.juneau.petstore.console.data.*;
 import org.apache.juneau.petstore.dto.*;
 import org.apache.juneau.petstore.rest.*;
@@ -49,7 +51,7 @@ class PetstoreSharedStore_Test extends TestBase {
 		return PetstoreSeed.create().populate(new PetStore(PetstoreSeed.DEFAULT_CLOCK));
 	}
 
-	@Rest(children={PetStoreResource.class, PetHtmlResource.class})
+	@Rest(children={PetStoreResource.class, PetstoreConsoleResource.class})
 	public static class Host extends BasicRestServletGroup {
 		private static final long serialVersionUID = 1L;
 
@@ -81,15 +83,20 @@ class PetstoreSharedStore_Test extends TestBase {
 		var c = MockRestClient.buildJsonLax(Host.class);
 		var created = c.post("/petstore/pets", new Pet().setName("Sharedpet").setSpecies(Species.FISH).setStatus(PetStatus.AVAILABLE))
 			.run().assertStatus(200).getContent().as(Pet.class);
-		var html = c.get("/petstore-html/card/" + created.getId()).accept("text/html").run().assertStatus(200).getContent().asString();
-		assertContains("Sharedpet", html);
+		var q = "{\"draw\":1,\"start\":0,\"length\":10,\"search\":{\"value\":\"Sharedpet\"},"
+			+ "\"columns\":[{\"data\":\"id\"},{\"data\":\"name\"}]}";
+		// A JsonMap body, so the JSON client sends the request object rather than a quoted string.
+		var r = new JsonMap(c.post("/console/pets/query", new JsonMap(q)).accept("application/json")
+			.run().assertStatus(200).getContent().asString());
+		assertBean(r, "recordsFiltered", "1");
+		assertBean(r.getList("data").getMap(0), "id,name", created.getId() + ",Sharedpet");
 	}
 
 	@Test void a03_standaloneResourceFallsBackToClassicStore() throws Exception {
 		MockRestClient.buildJsonLax(PetStoreResource.class).get("/pets/500").run().assertStatus(404);
 	}
 
-	// Plan Q12: does the store reach a resource two levels below the group that registered it?
+	// Does the store reach a resource two levels below the group that registered it?
 	@Test void a04_twoLevelsDeepSeesTheSeededStore() throws Exception {
 		var c = MockRestClient.buildJsonLax(DeepHost.class);
 		c.get("/mid/petstore/pets/500").run().assertStatus(200);
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/AboutRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/AboutRest_Test.java
new file mode 100644
index 0000000000..5e6fc5eb3c
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/AboutRest_Test.java
@@ -0,0 +1,79 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console;
+
+import static org.apache.juneau.petstore.console.PetstoreConsoleFixture.*;
+import static org.apache.juneau.rest.server.console.test.PageContractAssert.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * The About page and the utility-bean demos that used to be a standalone resource.
+ */
+@SuppressWarnings({
+	"resource" // MockRestClient is a no-op close.
+})
+class AboutRest_Test extends TestBase {
+
+	private static MockRestClient json() {
+		return MockRestClient.create(PetstoreConsoleFixture.Host.class).noTrace().ignoreErrors().header("Accept", "application/json").build();
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - page
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_pageRendersProseCardsAndLinks() throws Exception {
+		var html = page(client(), "/console/about");
+		assertPage(html).isValid().hasActiveNav("about").hasCardOrder("overview", "links", "beans");
+		assertContainsAll(html, "jc-prose", "href=\"/petstore/api\"", "href=\"/petstore-ui\"",
+			"href=\"https://juneau.apache.org/docs/topics/JuneauPetstore\"",
+			"href=\"/console/about/beans/BeanDescription\"", "href=\"/console/about/beans/Hyperlink\"", "href=\"/console/about/beans/SeeOtherRoot\"");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - utility beans
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_beansListsAllThree() throws Exception {
+		assertContainsAll(json().get("/console/about/beans").run().assertStatus(200).getContent().asString(),
+			"BeanDescription", "Hyperlink", "SeeOtherRoot");
+		assertContainsAll(client().get("/console/about/beans").accept("text/html").run().assertStatus(200).getContent().asString(),
+			"/console/about/beans/BeanDescription", "/console/about/beans/Hyperlink", "/console/about/beans/SeeOtherRoot");
+	}
+
+	@Test void b02_beanDescriptionDescribesPet() throws Exception {
+		assertContainsAll(json().get("/console/about/beans/BeanDescription").run().assertStatus(200).getContent().asString(),
+			Pet.class.getName(), "species", "photo");
+	}
+
+	@Test void b03_hyperlinkPointsBackToAbout() throws Exception {
+		assertContains("/console/about", json().get("/console/about/beans/Hyperlink").run().assertStatus(200).getContent().asString());
+	}
+
+	@Test void b04_seeOtherRootRedirects() throws Exception {
+		rawClient().get("/console/about/beans/SeeOtherRoot").run().assertStatus(303);
+	}
+
+	@Test void b05_apiLinkIsTheSwaggerPage() throws Exception {
+		client().get("/petstore/api").accept("text/html").run().assertStatus(200);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ConsoleWrites_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ConsoleWrites_Test.java
index 0842ba6d3a..2cadc6ed22 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ConsoleWrites_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ConsoleWrites_Test.java
@@ -19,6 +19,7 @@ package org.apache.juneau.petstore.console;
 import static org.apache.juneau.test.bct.BctAssertions.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.commons.settings.*;
 import org.apache.juneau.rest.server.*;
 import org.apache.juneau.rest.mock.classic.*;
 import org.junit.jupiter.api.*;
@@ -34,6 +35,11 @@ class ConsoleWrites_Test extends TestBase {
 		public String roles(RestRequest req) {
 			return String.join(",", ConsoleWrites.roles(req));
 		}
+
+		@RestGet(path="/actor")
+		public String actor(RestRequest req) {
+			return ConsoleWrites.actor(req);
+		}
 	}
 
 	private static MockRestClient client() {
@@ -55,4 +61,29 @@ class ConsoleWrites_Test extends TestBase {
 	@Test void a04_bearerPrefixIsCaseInsensitive() throws Exception {
 		assertString("admin", client().get("/roles").header("Authorization", "BEARER petstore-admin").run().assertStatus(200).getContent().asString());
 	}
+
+	@Test void b01_gateIsOffByDefault() throws Exception {
+		assertString("console", client().get("/actor").run().assertStatus(200).getContent().asString());
+	}
+
+	@Test void b02_settingTurnsGateOn() throws Exception {
+		Settings.get().setLocal(ConsoleWrites.SECURE_PROPERTY, "true");
+		try {
+			var c = client();
+			c.get("/actor").run().assertStatus(401);
+			assertString("console:admin", c.get("/actor").header("Authorization", "Bearer petstore-admin").run().assertStatus(200).getContent().asString());
+		} finally {
+			Settings.get().unsetLocal(ConsoleWrites.SECURE_PROPERTY);
+		}
+		assertString("console", client().get("/actor").run().assertStatus(200).getContent().asString());
+	}
+
+	@Test void b03_settingFalseKeepsGateOff() throws Exception {
+		Settings.get().setLocal(ConsoleWrites.SECURE_PROPERTY, "false");
+		try {
+			assertString("console", client().get("/actor").run().assertStatus(200).getContent().asString());
+		} finally {
+			Settings.get().unsetLocal(ConsoleWrites.SECURE_PROPERTY);
+		}
+	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssetOrder_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssetOrder_Test.java
index 41fcae002c..1a276fca5a 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssetOrder_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssetOrder_Test.java
@@ -136,7 +136,8 @@ class PetstoreAssetOrder_Test extends TestBase {
 	@Test void b01_syntheticCssBandViolation_isCaught() {
 		var reversed = "<link rel=\"stylesheet\" href=\"/juneau-config.css\">"
 			+ "<link rel=\"stylesheet\" href=\"/juneau-views.css\">";
-		var err = assertThrows(AssertionError.class, () -> assertBandOrder(cssBandSequence(reversed), "synthetic"));
+		var seq = cssBandSequence(reversed);
+		var err = assertThrows(AssertionError.class, () -> assertBandOrder(seq, "synthetic"));
 		assertTrue(err.getMessage().contains("juneau-views.css"), err.getMessage());
 	}
 
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssets_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssets_Test.java
new file mode 100644
index 0000000000..d595068811
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreAssets_Test.java
@@ -0,0 +1,98 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console;
+
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.petstore.console.browser.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.console.test.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Every stylesheet and script a console page links resolves on the host the runners and the browser tests mount.
+ *
+ * <p>
+ * The console chrome links its assets at context-root URLs ({@code /juneau-console/chrome.css}, the theme pack, the
+ * shell script), so they only load if the host mounts something at those paths.  MockRest page tests assert the
+ * hrefs; this test fetches them with {@link AssetResolutionAssert}.
+ */
+@SuppressWarnings({
+	"resource" // MockRestClient is a no-op close.
+})
+class PetstoreAssets_Test extends TestBase {
+
+	private static final MockRestClient C = MockRestClient.create(PetstoreTestServer.BrowserHost.class).noTrace().ignoreErrors().build();
+
+	private static String html(String path) throws Exception {
+		return C.get(path).accept("text/html").run().assertStatus(200).getContent().asString();
+	}
+
+	private static int status(String url) {
+		try {
+			return C.get(url).run().getStatusCode();
+		} catch (Exception e) {
+			throw new IllegalStateException(e);
+		}
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - every linked asset answers 200
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_everyPageAssetResolves() throws Exception {
+		for (var path : PetstorePages_ContractTest.PAGE_PATHS)
+			AssetResolutionAssert.assertAssetsResolve(path, html(path), PetstoreAssets_Test::status);
+	}
+
+	@Test void a02_theChromeLinksItsMountedAssets() throws Exception {
+		assertContainsAll(String.join("\n", AssetResolutionAssert.assetUrls(html("/console/store"))), "/juneau-console/chrome.css", "/juneau-console/juneau-console.js");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - the pages' own links and the DataTables dependencies
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_theReactFlavorEmbedsAnAppThatAnswers() throws Exception {
+		var html = html("/console/dev/flavors/react");
+		assertContains("src=\"/petstore-ui\"", html);
+		org.junit.jupiter.api.Assertions.assertEquals(200, status("/petstore-ui"));
+	}
+
+	@Test void b02_everyDatatablesPageLoadsJqueryAndDataTablesLocallyAfterTheToolkit() throws Exception {
+		// The views runtime binds datatables cards to jQuery and DataTables; Juneau does not bundle them, so the app serves its WebJars.
+		for (var path : java.util.List.of("/console/ops/jobs", "/console/ops/audit")) {
+			var html = html(path);
+			var toolkit = html.indexOf("juneau-views.js");
+			var jquery = html.indexOf("/console/vendor/jquery/3.7.1/jquery.min.js");
+			var dataTables = html.indexOf("/console/vendor/datatables.net/js/dataTables.min.js");
+			org.junit.jupiter.api.Assertions.assertTrue(toolkit > 0 && jquery > toolkit && dataTables > jquery, () -> path + ": views=" + toolkit + ", jquery=" + jquery + ", dataTables=" + dataTables);
+			org.junit.jupiter.api.Assertions.assertFalse(html.contains("cdn."), () -> path + " must not load a library from a CDN");
+		}
+	}
+
+	@Test void b03_theVendorLibrariesAreServedFromTheWebJars() throws Exception {
+		for (var url : java.util.List.of("/console/vendor/jquery/3.7.1/jquery.min.js", "/console/vendor/datatables.net/js/dataTables.min.js"))
+			C.get(url).run().assertStatus(200).assertHeader("Content-Type").asString().isContains("javascript");
+	}
+
+	@Test void b04_aVendorPathOutsideTheWebJarsDoesNotResolve() throws Exception {
+		C.get("/console/vendor/org/apache/juneau/petstore/console/VendorRest.class").run().assertStatus(404);
+		org.junit.jupiter.api.Assertions.assertEquals(404, status("/console/vendor/no-such/file.js"));
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleFixture.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleFixture.java
index 626cb6b366..fc28387550 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleFixture.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleFixture.java
@@ -32,6 +32,9 @@ import org.apache.juneau.rest.server.servlet.*;
  * 	<jk>var</jk> <jv>html</jv> = PetstoreConsoleFixture.<jsm>page</jsm>(PetstoreConsoleFixture.<jsm>client</jsm>(), <js>"/console/store"</js>);
  * </p>
  */
+@SuppressWarnings({
+	"resource" // client()/rawClient() return Closeables owned by the caller, and page() consumes its response; Eclipse JDT @Owning warning is by design.
+})
 public final class PetstoreConsoleFixture {
 
 	/** The host group. */
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleShell_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleShell_Test.java
index 6828417c8f..1c02a03a4c 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleShell_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreConsoleShell_Test.java
@@ -39,7 +39,7 @@ class PetstoreConsoleShell_Test extends TestBase {
 
 	@Test void a02_staticNavTree() throws Exception {
 		assertPage(page(client(), "/console/store"))
-			.hasNavPath("store").hasNavPath("pets").hasNavPath("orders").hasNavPath("users").hasNavPath("ops")
+			.hasNavPath("store").hasNavPath("pets").hasNavPath("orders").hasNavPath("users").hasNavPath("ops").hasNavPath("dev").hasNavPath("about")
 			.hasNavChildren("pets", "sold", "changes")
 			.hasNavChildren("ops", "jobs", "audit")
 			.hasNavHref("pets/sold", "/console/pets/sold");
@@ -60,9 +60,11 @@ class PetstoreConsoleShell_Test extends TestBase {
 		links.put("store", "/console/store");
 		links.put("ops/jobs", "/console/ops/jobs");
 		links.put("ops/audit", "/console/ops/audit");
+		for (var f : new String[]{"html", "freemarker", "mustache", "react"})
+			links.put("dev/flavors/" + f, "/console/dev/flavors/" + f);
 		for (var stub : ConsoleStubs.ALL)
 			links.put(stub.tab(), stub.href());
-		links.forEach((path, href) -> nav.hasNavHref(path, href));
+		links.forEach(nav::hasNavHref);
 		for (var href : links.values())
 			c.get(href).accept("text/html").run().assertStatus(200);
 	}
@@ -73,7 +75,8 @@ class PetstoreConsoleShell_Test extends TestBase {
 	}
 
 	@Test void a07_missingStoreBeanFailsLoudly() {
-		var e = org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, () -> new StoreRest().store());
+		var r = new StoreRest();
+		var e = org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, r::store);
 		assertContains("No PetStore bean is wired", e.getMessage());
 	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreFailLoud_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreFailLoud_Test.java
index 5cd487e771..1b6dd3d9a3 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreFailLoud_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstoreFailLoud_Test.java
@@ -34,6 +34,7 @@ import org.junit.jupiter.api.*;
  * landed are {@code @Disabled} with the gate that blocks them; none is invented here.
  */
 @SuppressWarnings({
+	"java:S1186", // Empty bodies are @Disabled placeholder rows; each names the gate that blocks it.
 	"resource" // MockRestClient is a no-op close.
 })
 class PetstoreFailLoud_Test extends TestBase {
@@ -63,7 +64,8 @@ class PetstoreFailLoud_Test extends TestBase {
 	}
 
 	@Test void a02_badNavPath() throws Exception {
-		assert500("bad-nav-path", "<@page tab='pets/nope'> does not match a visible <@node> path; known paths: 'store, pets, pets/sold, pets/changes, orders, users, ops, ops/jobs, ops/audit'.");
+		assert500("bad-nav-path", "<@page tab='pets/nope'> does not match a visible <@node> path; known paths: 'store, pets, pets/sold, pets/changes, orders, users, ops, ops/jobs, ops/audit, dev, dev/cards, dev/themes, "
+			+ "dev/flavors, dev/flavors/html, dev/flavors/freemarker, dev/flavors/mustache, dev/flavors/react, dev/secure, about'.");
 	}
 
 	@Test void a04_malformedJson5() throws Exception {
@@ -74,51 +76,51 @@ class PetstoreFailLoud_Test extends TestBase {
 		assert500("bad-custom-type-id", "<@card> type= must be one of html|datatables; got 'Gauge_1'.");
 	}
 
-	@Disabled("G-C2/C6: missing-slot E-code not landed")
+	@Disabled("Not implemented yet: an error code for a page that leaves a required slot empty")
 	@Test void a03_missingSlot() {}
 
-	@Disabled("G-C3: <@node under> / E-B14 not landed")
+	@Disabled("Not implemented yet: E-B14 for a <@node under=...> used outside a page or inside the navigation")
 	@Test void a05_nodeUnderOutsidePage() {}
 
-	@Disabled("G-C3: <@node under> / E-B14 not landed")
+	@Disabled("Not implemented yet: E-B14 for a <@node under=...> used outside a page or inside the navigation")
 	@Test void a06_nodeUnderInNavigation() {}
 
-	@Disabled("G-C3: <@node under> / E-B8 not landed")
+	@Disabled("Not implemented yet: E-B8 for a <@node under=...> naming an unknown parent")
 	@Test void a07_nodeUnderUnknownParent() {}
 
-	@Disabled("C1 E-3 is asserted by console-ui-freemarker (bad-dup-node); the production chrome has a fixed nav, so no petstore fixture can duplicate a sibling")
+	@Disabled("Duplicate-sibling error E-3 is asserted by the console FreeMarker bridge tests (bad-dup-node); the production chrome has a fixed nav, so no petstore fixture can duplicate a sibling")
 	@Test void a08_nodeDuplicateSibling() {}
 
-	@Disabled("G-C3: E-B12 not landed")
+	@Disabled("Not implemented yet: E-B12 for a page whose view has no registered renderer")
 	@Test void a09_missingViewRenderer() {}
 
-	@Disabled("G-C3: PageSpec.badge / E-B15 not landed")
+	@Disabled("Not implemented yet: E-B15 for a duplicate PageSpec.badge")
 	@Test void a10_badgeDuplicate() {}
 
-	@Disabled("G-C3: facts leaf / E-B16 not landed")
+	@Disabled("Not implemented yet: E-B16 for a duplicate facts leaf")
 	@Test void a11_factsDuplicate() {}
 
-	@Disabled("G-C3: <@facts> / E-B17 not landed")
+	@Disabled("Not implemented yet: E-B17 for a <@facts> with a bad type")
 	@Test void a12_factsBadType() {}
 
-	@Disabled("G-C6: visibleWhen unknown-op E-code not landed")
+	@Disabled("Not implemented yet: an error code for a visibleWhen expression with an unknown operator")
 	@Test void a13_visibleWhenBadOp() {}
 
-	@Disabled("G-C3: <@badge> scope / E-68 not landed")
+	@Disabled("Not implemented yet: E-68 for a <@badge> scoped to an unknown node")
 	@Test void a14_badgeScopeUnknownNode() {}
 
-	@Disabled("G-C2: CardTypeHandler / E-22 not landed")
+	@Disabled("Not implemented yet: E-22 for an adopter-registered CardTypeHandler")
 	@Test void a15_adopterCardTypeHandler() {}
 
-	@Disabled("G-C2: E-23 not landed (a datatables body setting title renders 200)")
+	@Disabled("Not implemented yet: E-23 (a datatables body that sets a reserved title key still renders 200)")
 	@Test void a16_reservedKey() {}
 
-	@Disabled("G-C2: E-25 not landed (markup in a datatables body renders 200)")
+	@Disabled("Not implemented yet: E-25 (markup in a datatables body still renders 200)")
 	@Test void a17_markupOnDatatables() {}
 
-	@Disabled("Blocked on P4 pet summary page (G-C3 <@node under>)")
+	@Disabled("Blocked on the pet summary page, which needs <@node under=...> support")
 	@Test void b01_scriptInPetNameIsEscaped() {}
 
-	@Disabled("G-C1 dev mode only rejects an invalid page contract (no overlay marker, no juneau.console.devMode property)")
+	@Disabled("Dev mode only rejects an invalid page contract (there is no overlay marker and no juneau.console.devMode property yet)")
 	@Test void b02_devModeOverlay() {}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstorePages_ContractTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstorePages_ContractTest.java
index 9a5b766c22..50670cc733 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstorePages_ContractTest.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/PetstorePages_ContractTest.java
@@ -41,9 +41,16 @@ class PetstorePages_ContractTest extends TestBase {
 	}
 
 	static final List<Page> PAGES = List.of(
-		new Page("P1", "/console/store", p -> p.hasActiveNav("store").hasCard("welcome", "html")),
-		new Page("P6", "/console/ops/jobs", p -> p.hasActiveNav("ops", "jobs").hasCard("jobs", "datatables")),
-		new Page("P7", "/console/ops/audit", p -> p.hasActiveNav("ops", "audit").hasCard("audit", "datatables"))
+		new Page("store", "/console/store", p -> p.hasActiveNav("store").hasCard("welcome", "html")),
+		new Page("jobs", "/console/ops/jobs", p -> p.hasActiveNav("ops", "jobs").hasCard("jobs", "datatables")),
+		new Page("audit", "/console/ops/audit", p -> p.hasActiveNav("ops", "audit").hasCard("audit", "datatables")),
+		new Page("flavors-html", "/console/dev/flavors/html", p -> p.hasActiveNav("dev", "flavors", "html").hasCardOrder("caption", "flavor")),
+		new Page("flavors-freemarker", "/console/dev/flavors/freemarker", p -> p.hasActiveNav("dev", "flavors", "freemarker").hasCardOrder("caption", "flavor")),
+		new Page("flavors-mustache", "/console/dev/flavors/mustache", p -> p.hasActiveNav("dev", "flavors", "mustache").hasCardOrder("caption", "flavor")),
+		new Page("flavors-react", "/console/dev/flavors/react", p -> p.hasActiveNav("dev", "flavors", "react").hasCardOrder("caption", "flavor")),
+		new Page("secure", "/console/dev/secure", p -> p.hasActiveNav("dev", "secure").hasCardOrder("about", "try")),
+		new Page("about", "/console/about", p -> p.hasActiveNav("about").hasCardOrder("overview", "links", "beans")),
+		new Page("themes", "/console/dev/themes", p -> p.hasActiveNav("dev", "themes").hasCardOrder("swatches", "picker", "authoring").hasTheme("open"))
 		// Later tasks append rows here.
 	);
 
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/Covers.java
similarity index 51%
copy from juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
copy to juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/Covers.java
index 059865e006..58adfc7a4f 100644
--- a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/Covers.java
@@ -14,14 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.juneau.petstore.console.browser;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
 /**
- * Test helpers for console pages: {@link org.apache.juneau.rest.server.console.test.PageContractAssert} asserts on the
- * {@code #juneau-page} contract of a rendered page instead of on rendered chrome HTML.
+ * Marks a browser test as covering one or more console parity items, on the given pages.
  *
- * <p>
- * Main scope on purpose (C1-D3): adopters already depend on {@code juneau-rest-server-console-ui}. The package has no
- * test-framework dependency; failures are plain {@link java.lang.AssertionError}s.
- *
- * @since 10.0.0
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<ja>@Covers</ja>(value=<js>"async-job"</js>, pages=<js>"/console/ops/jobs"</js>)
+ * 	<jk>class</jk> PetstoreAsyncJob_BrowserTest <jk>extends</jk> TestBase { ... }
+ * </p>
  */
-package org.apache.juneau.rest.server.console.test;
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+public @interface Covers {
+
+	/** @return The parity ids, e.g. {@code "pending-changes-badge"}. */
+	String[] value();
+
+	/** @return The console paths that exercise them; each must render. */
+	String[] pages();
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/Harness_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/Harness_BrowserTest.java
new file mode 100644
index 0000000000..cb2db1f72e
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/Harness_BrowserTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * Smoke test for the browser harness: one console page loads in Chromium from the live test server with no
+ * script errors.
+ */
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class Harness_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	private static Map<String,Map<String,Object>> report;
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		report = run("harness", base, List.of(
+			Map.of("name", "store", "path", "/console/store",
+				"queries", Map.of("title", "document.title", "pages", "document.querySelectorAll('#juneau-page').length"))));
+	}
+
+	@Test void a01_storeLoadsCleanly() {
+		var c = assertClean(report, "store");
+		assertString("1", String.valueOf(query(c, "pages")));
+		assertContains("Juneau Petstore", query(c, "title"));
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreAsyncJob_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreAsyncJob_BrowserTest.java
new file mode 100644
index 0000000000..ff13ac3e92
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreAsyncJob_BrowserTest.java
@@ -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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * P6 in the real browser: a double submit with one key returns one job, the job finishes, the polled jobs table
+ * shows it, and an unknown id is 404.
+ */
+@Covers(value="async-job", pages="/console/ops/jobs")
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class PetstoreAsyncJob_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	private static final String SUBMIT_TWICE = "(async () => {"
+		+ " const key = 'browser-' + Date.now();"
+		+ " const post = () => fetch('/console/ops/jobs/restock', {method: 'POST', credentials: 'same-origin',"
+		+ "   headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},"
+		+ "   body: JSON.stringify({perSpecies: {DOG: 1}, idempotencyKey: key})});"
+		+ " const a = await post(), b = await post();"
+		+ " const ja = await a.json(), jb = await b.json();"
+		+ " window.__jobId = ja.jobId;"
+		+ " let state = 'RUNNING';"
+		+ " for (let i = 0; i < 100 && state === 'RUNNING'; i++) {"
+		+ "   await new Promise(r => setTimeout(r, 100));"
+		+ "   state = (await (await fetch('/console/ops/jobs/' + ja.jobId, {headers: {'Accept': 'application/json'}})).json()).state;"
+		+ " }"
+		+ " return {a: a.status, b: b.status, same: ja.jobId === jb.jobId, state};"
+		+ "})()";
+
+	private static Map<String,Map<String,Object>> report;
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		report = run("async-job", base, List.of(
+			Map.of("name", "jobs", "path", "/console/ops/jobs", "allowFailedLoads", true,
+				"actions", List.of(
+					Map.of("waitFor", "document.querySelector('#jobs-body table')"),
+					Map.of("evaluate", "window.__submit = " + SUBMIT_TWICE),
+					Map.of("waitFor", "window.__jobId && document.querySelector('#jobs-body').textContent.includes(window.__jobId)", "timeoutMs", 15000)),
+				"queries", Map.of("submit", "window.__submit"),
+				"fetches", Map.of("unknown", "/console/ops/jobs/no-such-job"))));
+	}
+
+	@Test void a01_doubleSubmitIsOneJobThatFinishes() {
+		var c = assertClean(report, "jobs");
+		assertBean(query(c, "submit"), "a,b,same,state", "202,202,true,DONE");
+	}
+
+	@Test void a02_pollingTableShowsTheJob() {
+		assertClean(report, "jobs"); // timeouts is empty, so the waitFor on the table text succeeded
+	}
+
+	@SuppressWarnings("unchecked") // fetches values are {status, body}.
+	@Test void a03_unknownJobIs404() {
+		var f = (Map<String,Object>) ((Map<String,Object>) assertClean(report, "jobs").get("fetches")).get("unknown");
+		assertBean(f, "status", "404");
+		assertContains("Unknown job 'no-such-job'", f.get("body"));
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreBrowser.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreBrowser.java
new file mode 100644
index 0000000000..73deade75c
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreBrowser.java
@@ -0,0 +1,135 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static java.nio.charset.StandardCharsets.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.marshall.marshaller.*;
+
+/**
+ * Runs {@code petstore-browser.cjs} over a list of cases and returns its per-case report.
+ *
+ * <p>
+ * A case is a JSON object: {@code name}, {@code path} (appended to the base URL), optional {@code actions}
+ * (each {@code {click|fill|press|waitFor|evaluate|screenshot: ...}}), {@code queries} (name to a JS expression
+ * evaluated in the page after the actions), {@code fetches} (name to a URL fetched from the page) and
+ * {@code capture} (a URL substring whose requests are recorded with their POST bodies) and
+ * {@code allowFailedLoads} (ignore Chromium's "Failed to load resource" line for a deliberate 401/404).
+ *
+ * <p>
+ * The report for each case has {@code status} (the navigation's HTTP status), {@code jsFailures} (page errors
+ * and {@code console.error} lines), {@code deprecations} ({@code console.warn} lines matching
+ * {@code is deprecated}), {@code queries}, {@code fetches}, {@code requests} and {@code timeouts}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jk>var</jk> <jv>report</jv> = PetstoreBrowser.<jsm>run</jsm>(<js>"nav"</js>, <jv>base</jv>, List.<jsm>of</jsm>(
+ * 		Map.<jsm>of</jsm>(<js>"name"</js>, <js>"store"</js>, <js>"path"</js>, <js>"/console/store"</js>,
+ * 			<js>"queries"</js>, Map.<jsm>of</jsm>(<js>"title"</js>, <js>"document.title"</js>))));
+ * 	PetstoreBrowser.<jsm>assertClean</jsm>(<jv>report</jv>, <js>"store"</js>);
+ * </p>
+ */
+public final class PetstoreBrowser {
+
+	/** System property the {@code js-tests} profile sets to enable the {@code *_BrowserTest} classes. */
+	public static final String GATE = "juneau.jsTests";
+
+	/** The skip reason shown when the gate is off. */
+	public static final String DISABLED = "Browser tests are opt-in; run with `mvn -Pjs-tests -pl juneau-petstore/juneau-petstore-core -am test`";
+
+	private PetstoreBrowser() {}
+
+	/**
+	 * @param suite A short name for the files this run writes under {@code target/js}.
+	 * @param baseUrl The server base URL.
+	 * @param cases The cases.
+	 * @return Case name to that case's report.
+	 * @throws Exception If the harness can't run or exits non-zero.
+	 */
+	public static Map<String,Map<String,Object>> run(String suite, String baseUrl, List<Map<String,Object>> cases) throws Exception {
+		var dir = Path.of(required("juneau.jsTests.dir"));
+		var harness = Path.of(required("juneau.jsTests.harness"));
+		var in = dir.resolve(suite + "-cases.json");
+		var out = dir.resolve(suite + "-stdout.json");
+		var err = dir.resolve(suite + "-stderr.txt");
+		Files.createDirectories(dir.resolve("snapshots"));
+		Files.writeString(in, Json.of(Map.of("baseUrl", baseUrl, "snapshots", dir.resolve("snapshots").toString(), "cases", cases)), UTF_8);
+
+		var pb = new ProcessBuilder(List.of(System.getProperty("juneau.jsTests.node", "node"), harness.toString(), in.toString()))
+			.redirectOutput(out.toFile()).redirectError(err.toFile());
+		pb.environment().put("NODE_PATH", dir.resolve("node_modules").toString());
+		pb.environment().put("PLAYWRIGHT_BROWSERS_PATH", required("juneau.jsTests.browsers"));
+		var p = pb.start();
+		if (! p.waitFor(3, TimeUnit.MINUTES)) {
+			p.destroyForcibly();
+			fail("Harness '" + suite + "' did not finish within 3m; stderr:\n" + quietRead(err));
+		}
+		assertEquals(0, p.exitValue(), () -> "Harness '" + suite + "' exited non-zero; stderr:\n" + quietRead(err));
+
+		@SuppressWarnings("unchecked") // The harness writes {cases:{name:{...}}}.
+		var r = (Map<String,Map<String,Object>>) Json.to(Files.readString(out), Map.class).get("cases");
+		assertNotNull(r, () -> "Harness '" + suite + "' wrote no 'cases' object; stdout:\n" + quietRead(out));
+		return r;
+	}
+
+	/**
+	 * Asserts the case loaded with status 200, no script errors, no deprecation warnings and no timeouts.
+	 *
+	 * @param report The {@link #run} result.
+	 * @param name The case name.
+	 * @return The case's report.
+	 */
+	public static Map<String,Object> assertClean(Map<String,Map<String,Object>> report, String name) {
+		var c = report.get(name);
+		assertNotNull(c, () -> "No report for case '" + name + "'; cases: " + report.keySet());
+		assertEquals(200, ((Number) c.get("status")).intValue(), () -> "Case '" + name + "' status: " + c);
+		assertEquals(List.of(), c.get("jsFailures"), () -> "Case '" + name + "' logged script errors: " + c.get("jsFailures"));
+		assertEquals(List.of(), c.get("deprecations"), () -> "Case '" + name + "' used deprecated client APIs: " + c.get("deprecations"));
+		assertEquals(List.of(), c.get("timeouts"), () -> "Case '" + name + "' timed out waiting for: " + c.get("timeouts"));
+		return c;
+	}
+
+	/**
+	 * @param c One case's report.
+	 * @param key The query name.
+	 * @return The query's value as returned by the page.
+	 */
+	@SuppressWarnings("unchecked") // queries is always a JSON object.
+	public static Object query(Map<String,Object> c, String key) {
+		return ((Map<String,Object>) c.get("queries")).get(key);
+	}
+
+	private static String required(String name) {
+		var v = System.getProperty(name);
+		assertNotNull(v, () -> "-D" + name + " not set; the js-tests profile is responsible for providing it");
+		return v;
+	}
+
+	private static String quietRead(Path p) {
+		try {
+			return Files.readString(p);
+		} catch (IOException e) {
+			return "<unreadable: " + e + ">";
+		}
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreCoverage_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreCoverage_Test.java
new file mode 100644
index 0000000000..efc263c02f
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreCoverage_Test.java
@@ -0,0 +1,118 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.PetstoreConsoleFixture.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.nio.file.*;
+import java.util.*;
+import java.util.stream.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Parity gate: every console parity item has a petstore page and a browser test, or names the gate it
+ * waits on.
+ */
+@SuppressWarnings({
+	"resource" // Each render's response is fully consumed by assertStatus(); Eclipse JDT @Owning warning is by design.
+})
+class PetstoreCoverage_Test extends TestBase {
+
+	/** The parity ids. */
+	static final List<String> PARITY = List.of("export", "charts", "visibility-rules", "pending-changes-badge", "bulk-select", "per-view-tab-restriction", "url-state", "job-log-card", "confirm-dialogs", "inline-edit", "related-list", "async-job", "depth-4-nav");
+
+	/** Every browser test class; a01 proves the list is complete. */
+	static final List<Class<?>> BROWSER_TESTS = List.of(
+		Harness_BrowserTest.class,
+		PetstoreNav_BrowserTest.class,
+		PetstoreFlavors_BrowserTest.class,
+		PetstoreSecure_BrowserTest.class,
+		PetstoreThemes_BrowserTest.class,
+		PetstoreAsyncJob_BrowserTest.class,
+		PetstoreDatatable_BrowserTest.class);
+
+	/** Parity ids not yet covered, each with what it waits on.  Remove a row when its browser test lands. */
+	static final Map<String,String> PENDING = Map.ofEntries(
+		Map.entry("export", "P2 export: needs console custom card types and ribbon export"),
+		Map.entry("charts", "P1/P8 charts: needs the console hook namespaces and the P1 dashboard charts"),
+		Map.entry("visibility-rules", "P4 visibility rules: needs the console hook namespaces and the P4 pet page"),
+		Map.entry("pending-changes-badge", "pending-changes badge: needs the console hook namespaces and P4 change staging"),
+		Map.entry("bulk-select", "P2 bulk select: needs console custom card types"),
+		Map.entry("per-view-tab-restriction", "per-view tab restriction: needs BeanQuery view restriction and the orders/users query endpoints"),
+		Map.entry("url-state", "URL state: needs the console hook namespaces"),
+		Map.entry("job-log-card", "job-log card: needs the console's stock job-log card type"),
+		Map.entry("confirm-dialogs", "confirm renderers and dialogs: needs the console hook namespaces"),
+		Map.entry("inline-edit", "inline edit: needs the console hook namespaces and the P4 pet page"),
+		Map.entry("related-list", "related list: needs the console hook namespaces and the P4 pet page"),
+		Map.entry("depth-4-nav", "depth-4 nav: needs the P4 pet page"));
+
+	private static Map<String,List<Class<?>>> covered() {
+		var m = new TreeMap<String,List<Class<?>>>();
+		for (var c : BROWSER_TESTS) {
+			var a = c.getAnnotation(Covers.class);
+			if (a != null)
+				for (var id : a.value())
+					m.computeIfAbsent(id, k -> new ArrayList<>()).add(c);
+		}
+		return m;
+	}
+
+	@Test void a01_everyBrowserTestIsListed() throws Exception {
+		Set<String> onDisk;
+		try (var s = Files.walk(Path.of("src/test/java/org/apache/juneau/petstore"))) {
+			onDisk = s.map(p -> p.getFileName().toString()).filter(n -> n.endsWith("_BrowserTest.java"))
+				.map(n -> n.substring(0, n.length() - 5)).collect(Collectors.toCollection(TreeSet::new));
+		}
+		var listed = BROWSER_TESTS.stream().map(Class::getSimpleName).collect(Collectors.toCollection(TreeSet::new));
+		assertList(() -> "BROWSER_TESTS is out of date", listed, onDisk.toArray());
+	}
+
+	@Test void a02_everyParityItemIsCoveredOrPending() {
+		var covered = covered();
+		var problems = new ArrayList<String>();
+		for (var id : PARITY) {
+			var c = covered.containsKey(id);
+			var p = PENDING.containsKey(id);
+			if (! (c || p))
+				problems.add(String.format("Parity item '%s' has no petstore page or browser test", id));
+			if (c && p)
+				problems.add(String.format("Parity item '%s' is covered by %s but still listed as pending ('%s'); remove the PENDING row", id, covered.get(id), PENDING.get(id)));
+		}
+		assertString("", String.join("\n", problems));
+	}
+
+	@Test void a03_noUnknownIds() {
+		var unknown = new TreeSet<String>(covered().keySet());
+		unknown.addAll(PENDING.keySet());
+		unknown.removeAll(PARITY);
+		assertString("", String.join(",", unknown));  // unknown parity ids
+	}
+
+	@Test void a04_everyCoveringPageRenders() throws Exception {
+		try (var c = client()) {
+			for (var t : BROWSER_TESTS) {
+				var a = t.getAnnotation(Covers.class);
+				if (a != null)
+					for (var path : a.pages())
+						c.get(path).accept("text/html").run().assertStatus(200);
+			}
+		}
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreDatatable_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreDatatable_BrowserTest.java
new file mode 100644
index 0000000000..30fe0b30a2
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreDatatable_BrowserTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * P7 Audit in the real browser: the server-mode table POSTs a DataTables request to the BeanQuery endpoint, and a
+ * global search sends a new request carrying the search value.
+ */
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class PetstoreDatatable_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	/** An audit entity the seed data guarantees (its 300 audit rows are Pet, Order and User entries), so the search has hits. */
+	private static final String SEARCH = "Order";
+
+	private static final String ROWS = "[...document.querySelectorAll('#audit-body tbody tr')].filter(tr => !tr.querySelector('.dt-empty')).map(tr => tr.textContent)";
+
+	private static Map<String,Map<String,Object>> report;
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		report = run("datatable", base, List.of(
+			Map.of("name", "audit", "path", "/console/ops/audit", "capture", "/console/ops/audit/query",
+				"actions", List.of(
+					Map.of("waitForCaptured", 1),
+					Map.of("waitFor", "document.querySelector('#audit-body tbody tr td')"),
+					Map.of("fill", List.of("#audit-body input[type=search]", SEARCH)),
+					Map.of("waitForCaptured", 2, "timeoutMs", 5000),
+					// The POST returning is not the redraw: wait until the table shows only rows the search matched.
+					Map.of("waitFor", "(() => { const r = [...document.querySelectorAll('#audit-body tbody tr')]; return r.length > 0 && r.every(tr => !tr.querySelector('.dt-empty') && tr.textContent.includes('" + SEARCH + "')); })()")),
+				"queries", Map.of("rows", ROWS))));
+	}
+
+	@SuppressWarnings("unchecked") // requests is a JSON array of {url, method, postData}.
+	private static List<Map<String,Object>> requests() {
+		return (List<Map<String,Object>>) assertClean(report, "audit").get("requests");
+	}
+
+	private static Map<?,?> body(Map<String,Object> request) throws Exception {
+		return Json.to((String) request.get("postData"), Map.class);
+	}
+
+	@Test void a01_firstLoadPostsADataTablesRequest() throws Exception {
+		var r = requests().get(0);
+		assertString("POST", r.get("method"));
+		var b = body(r);
+		assertBean(b, "draw,start,length", "1,0,25");  // views toolkit default page size: juneau-views.js PAGE_SIZE_OPTIONS[0] = 25
+		assertContainsAll(String.valueOf(b.get("columns")), "at", "actor", "action", "entity", "entityId", "detail");
+	}
+
+	@Test void a02_globalSearchSendsTheValue() throws Exception {
+		var reqs = requests();
+		assertContains(SEARCH, String.valueOf(body(reqs.get(reqs.size() - 1)).get("search")));
+	}
+
+	@Test void a03_onlyRowsMatchingTheSearchRender() {
+		var rows = (List<?>) query(assertClean(report, "audit"), "rows");  // DataTables' "no data" row is filtered out
+		assertNotEmpty(rows);
+		for (var row : rows)
+			assertContains(SEARCH, row);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreFlavors_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreFlavors_BrowserTest.java
new file mode 100644
index 0000000000..8fb66b25f4
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreFlavors_BrowserTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * P12 in the real browser: each server flavor's fragment renders the same pets inside the console chrome with no
+ * second chrome, and the React subtab iframes {@code /petstore-ui}.
+ */
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class PetstoreFlavors_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	private static final String TABLE = "#flavor table.petstore-flavor";
+
+	private static Map<String,Map<String,Object>> report;
+
+	private static Map<String,Object> serverFlavor(String engine) {
+		return Map.of("name", engine, "path", "/console/dev/flavors/" + engine,
+			"actions", List.of(Map.of("waitFor", "document.querySelector('" + TABLE + " td')")),
+			"queries", Map.of(
+				"names", "[...document.querySelectorAll('" + TABLE + " tr td:first-child')].map(td => td.textContent.trim())",
+				"flavor", "document.querySelector('" + TABLE + "').getAttribute('data-flavor')",
+				"islands", "document.querySelectorAll('#juneau-page').length",
+				"navs", "document.querySelectorAll('nav.juneau-page-nav').length"));
+	}
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		report = run("flavors", base, List.of(
+			serverFlavor("html"), serverFlavor("freemarker"), serverFlavor("mustache"),
+			Map.of("name", "react", "path", "/console/dev/flavors/react",
+				"actions", List.of(Map.of("waitFor", "document.querySelector('#flavor iframe')")),
+				"queries", Map.of(
+					"frame", "new URL(document.querySelector('#flavor iframe').src).pathname",
+					"title", "document.querySelector('#flavor iframe').title"))));
+	}
+
+	@SuppressWarnings("unchecked") // names is a JSON array of strings.
+	private static List<String> names(String engine) {
+		return (List<String>) query(assertClean(report, engine), "names");
+	}
+
+	@Test void a01_threeFlavorsShowTheSameTenPets() {
+		var html = names("html");
+		assertSize(10, html);
+		assertList(() -> "freemarker differs from html", names("freemarker"), html.toArray());
+		assertList(() -> "mustache differs from html", names("mustache"), html.toArray());
+	}
+
+	@Test void a02_eachFragmentIsItsOwnFlavorWithNoSecondChrome() {
+		for (var e : List.of("html", "freemarker", "mustache")) {
+			var c = assertClean(report, e);
+			assertString(e, query(c, "flavor"));
+			assertString("1", String.valueOf(query(c, "islands")));
+			assertString("1", String.valueOf(query(c, "navs")));
+		}
+	}
+
+	@Test void a03_reactIframesTheApp() {
+		var c = assertClean(report, "react");
+		assertString("/petstore-ui", query(c, "frame"));
+		assertString("Petstore React app", query(c, "title"));
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreNav_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreNav_BrowserTest.java
new file mode 100644
index 0000000000..ec198bec8c
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreNav_BrowserTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * Nav in the real browser: one children row per selected ancestor and {@code aria-current} on each path link.
+ */
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class PetstoreNav_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	private static final String CURRENT = "[...document.querySelectorAll('[data-juneau-nav-id][aria-current=\"page\"]')].map(a => a.getAttribute('data-juneau-nav-id'))";
+	private static final String CHILD_ROWS = "document.querySelectorAll('.juneau-page-nav-children').length";
+
+	private static Map<String,Map<String,Object>> report;
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		report = run("nav", base, List.of(
+			Map.of("name", "flavor-html", "path", "/console/dev/flavors/html", "queries", Map.of("current", CURRENT, "rows", CHILD_ROWS)),
+			Map.of("name", "about", "path", "/console/about", "queries", Map.of("current", CURRENT, "rows", CHILD_ROWS)),
+			Map.of("name", "click-dev", "path", "/console/store",
+				"actions", List.of(
+					Map.of("click", "[data-juneau-nav-id=\"dev\"]"),
+					Map.of("waitFor", "location.pathname.startsWith('/console/dev/')")),
+				"queries", Map.of("current", CURRENT, "path", "location.pathname"))));
+	}
+
+	@Test void a01_depth3PathIsCurrentAtEachLevel() {
+		var c = assertClean(report, "flavor-html");
+		assertList(query(c, "current"), "dev", "flavors", "html");
+		assertString("2", String.valueOf(query(c, "rows")));
+	}
+
+	@Test void a02_leafSectionHasNoChildRow() {
+		var c = assertClean(report, "about");
+		assertList(query(c, "current"), "about");
+		assertString("0", String.valueOf(query(c, "rows")));
+	}
+
+	@Test void a03_clickingASectionNavigates() {
+		var c = assertClean(report, "click-dev");
+		assertString("/console/dev/cards", query(c, "path"));
+		assertList(query(c, "current"), "dev", "cards");
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreSecure_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreSecure_BrowserTest.java
new file mode 100644
index 0000000000..0c9a94a459
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreSecure_BrowserTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * P13 in the real browser: the "Try it" buttons get 401 without the demo token, 200 with it, 401 with a wrong one.
+ */
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class PetstoreSecure_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	private static Map<String,Map<String,Object>> report;
+
+	private static Map<String,Object> click(String token) {
+		return Map.of("name", token, "path", "/console/dev/secure", "allowFailedLoads", true,
+			"actions", List.of(
+				Map.of("click", "[data-secure-call=\"" + token + "\"]"),
+				Map.of("waitFor", "document.getElementById('secure-result').getAttribute('data-status')")),
+			"queries", Map.of(
+				"status", "document.getElementById('secure-result').getAttribute('data-status')",
+				"text", "document.getElementById('secure-result').textContent"));
+	}
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		report = run("secure", base, List.of(click("none"), click("petstore-user"), click("wrong-token")));
+	}
+
+	@Test void a01_noToken401() {
+		assertString("401", query(assertClean(report, "none"), "status"));
+	}
+
+	@Test void a02_demoToken200NamesAlice() {
+		var c = assertClean(report, "petstore-user");
+		assertString("200", query(c, "status"));
+		assertContains("alice", query(c, "text"));
+	}
+
+	@Test void a03_wrongToken401() {
+		assertString("401", query(assertClean(report, "wrong-token"), "status"));
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreTestServer.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreTestServer.java
new file mode 100644
index 0000000000..41cb7a5285
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreTestServer.java
@@ -0,0 +1,108 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import java.io.*;
+import java.net.*;
+import java.net.http.*;
+import java.net.http.HttpResponse.*;
+import java.time.*;
+
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.microservice.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.petstore.console.data.*;
+import org.apache.juneau.petstore.rest.*;
+import org.apache.juneau.petstore.service.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.servlet.*;
+
+import jakarta.servlet.*;
+
+/**
+ * The browser tests' server: the {@code /console} tree, the {@code /petstore} API and the {@code /petstore-ui}
+ * React app over one seeded store, in a real Jetty bound to an ephemeral port.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<ja>@RegisterExtension</ja>
+ * 	<jk>static</jk> MicroserviceTestFixture <jv>server</jv> = PetstoreTestServer.<jsm>fixture</jsm>();
+ *
+ * 	<ja>@BeforeAll</ja>
+ * 	<jk>static void</jk> boot() <jk>throws</jk> Exception {
+ * 		PetstoreTestServer.<jsm>awaitReady</jsm>(<jv>server</jv>);
+ * 	}
+ * </p>
+ */
+@SuppressWarnings({
+	"java:S2925", // awaitReady() needs a back-off between retries; there is no readiness event to await.
+	"java:S8692" // awaitReady() polls a real server against a wall-clock deadline.
+})
+public final class PetstoreTestServer {
+
+	/** The root group: the same children as the runners' RootResources, minus the runner-only admin resources. */
+	@Rest(children={PetstoreConsoleResource.class, ConsoleAssetsRest.class, PetStoreResource.class, PetstoreUiResource.class})
+	public static class BrowserHost extends BasicRestServletGroup {
+		private static final long serialVersionUID = 1L;
+
+		/** @return The seeded store shared by every child. */
+		@Bean public PetStore petStore() {
+			return PetstoreSeed.create().populate(new PetStore(PetstoreSeed.DEFAULT_CLOCK));
+		}
+	}
+
+	/** Contributes {@link BrowserHost} to the fixture's Jetty. */
+	@Configuration
+	public static class BrowserConfig {
+		/** @return The root servlet. */
+		@Bean public Servlet root() {
+			return new BrowserHost();
+		}
+	}
+
+	private PetstoreTestServer() {}
+
+	/** @return A new fixture; register it with {@code @RegisterExtension} on a static field. */
+	public static MicroserviceTestFixture fixture() {
+		return MicroserviceTestFixture.create().configurations(BrowserConfig.class);
+	}
+
+	/**
+	 * Waits until {@code /console/store} answers 200, absorbing the first request's one-time REST setup.
+	 *
+	 * @param f The started fixture.
+	 * @return The base URL, without a trailing slash.
+	 * @throws Exception If the server is not ready within 30 seconds.
+	 */
+	public static String awaitReady(MicroserviceTestFixture f) throws Exception {
+		var base = f.getRootUrl().toString().replaceAll("/$", "");
+		var http = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
+		var deadline = Instant.now().plusSeconds(30);
+		Exception last = null;
+		while (Instant.now().isBefore(deadline)) {
+			try {
+				var req = HttpRequest.newBuilder(URI.create(base + "/console/store")).timeout(Duration.ofSeconds(20)).header("Accept", "text/html").GET().build();
+				if (http.send(req, BodyHandlers.discarding()).statusCode() == 200)
+					return base;
+			} catch (IOException e) {  // timeout, refused, reset or EOF while Jetty is still starting: retry until the deadline
+				last = e;
+			}
+			Thread.sleep(250);
+		}
+		throw new IllegalStateException("Petstore test server did not become ready within 30s at '" + base + "'", last);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreThemes_BrowserTest.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreThemes_BrowserTest.java
new file mode 100644
index 0000000000..57e1d85aa6
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/browser/PetstoreThemes_BrowserTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.browser;
+
+import static org.apache.juneau.petstore.console.browser.PetstoreBrowser.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.microservice.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.condition.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * P9 in the real browser: each stock theme resolves to a distinct set of token values, the custom theme's leaf and
+ * alias resolve, and every theme is screenshotted to {@code target/js/snapshots} (CI artifacts only, OQ9).
+ */
+@EnabledIfSystemProperty(named=PetstoreBrowser.GATE, matches="true", disabledReason=PetstoreBrowser.DISABLED)
+class PetstoreThemes_BrowserTest extends TestBase {
+
+	@RegisterExtension
+	static MicroserviceTestFixture server = PetstoreTestServer.fixture();
+
+	private static final List<String> STOCK = List.of("open", "light-red", "light-brown", "red", "gray");
+	// Tokens every stock pack defines and that differ between open and gray (checked against juneau-theme-*.css).
+	private static final String TOKENS = "(() => { const s = getComputedStyle(document.documentElement);"
+		+ " return ['--jc-chrome-bg', '--jc-border', '--jc-text', '--jc-accent-selected', '--jc-pill-red-bg'].map(n => s.getPropertyValue(n).trim()); })()";
+	// The custom theme's own list: --jc-tab-bar-bg is an alias declared only by the custom <@theme> block.
+	private static final String CUSTOM_TOKENS = "(() => { const s = getComputedStyle(document.documentElement);"
+		+ " return ['--jc-pill-red-bg', '--jc-tab-bar-bg', '--jc-card-bg'].map(n => s.getPropertyValue(n).trim()); })()";
+
+	private static Map<String,Map<String,Object>> report;
+
+	@BeforeAll
+	static void probe() throws Exception {
+		var base = PetstoreTestServer.awaitReady(server);
+		var cases = new ArrayList<Map<String,Object>>();
+		for (var t : concat(STOCK, "custom"))
+			cases.add(Map.of("name", t, "path", "/console/dev/themes?theme=" + t,
+				"actions", List.of(Map.of("screenshot", "theme-" + t + ".png")),
+				"queries", Map.of("tokens", "custom".equals(t) ? CUSTOM_TOKENS : TOKENS)));
+		report = run("themes", base, cases);
+	}
+
+	private static List<String> concat(List<String> l, String x) {
+		var out = new ArrayList<>(l);
+		out.add(x);
+		return out;
+	}
+
+	@SuppressWarnings("unchecked") // tokens is a JSON array of strings.
+	private static List<String> tokens(String theme) {
+		return (List<String>) query(assertClean(report, theme), "tokens");
+	}
+
+	@Test void a01_everyTokenResolvesInEveryTheme() {
+		for (var t : concat(STOCK, "custom"))
+			for (var v : tokens(t))
+				assertNotEmpty(() -> "Theme '" + t + "' left a token unresolved: " + tokens(t), v);
+	}
+
+	@Test void a02_stockThemesAreDistinct() {
+		var seen = new HashSet<List<String>>();
+		for (var t : STOCK)
+			seen.add(tokens(t));
+		assertSize(STOCK.size(), seen);
+	}
+
+	@Test void a03_customLeafAndAliasResolve() {
+		var c = tokens("custom");
+		assertString("#fdeceb", c.get(0));  // --jc-pill-red-bg leaf override
+		assertString(c.get(2), c.get(1));   // --jc-tab-bar-bg follows --jc-card-bg
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/DevNav_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/DevNav_Test.java
new file mode 100644
index 0000000000..5af30a5673
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/DevNav_Test.java
@@ -0,0 +1,68 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import static org.apache.juneau.petstore.console.PetstoreConsoleFixture.*;
+import static org.apache.juneau.rest.server.console.test.PageContractAssert.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * The Developer and About nav nodes exist on every page, and their routes resolve.
+ */
+@SuppressWarnings({
+	"resource" // MockRestClient is a no-op close.
+})
+class DevNav_Test extends TestBase {
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - nav tree
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_developerNodeAndChildren() throws Exception {
+		assertPage(page(client(), "/console/store"))
+			.hasNavPath("dev")
+			.hasNavChildren("dev", "cards", "themes", "flavors", "secure")
+			.hasNavChildren("dev/flavors", "html", "freemarker", "mustache", "react")
+			.hasNavHref("dev", "/console/dev/cards")
+			.hasNavHref("dev/flavors", "/console/dev/flavors/html")
+			.hasNavHref("dev/flavors/react", "/console/dev/flavors/react");
+	}
+
+	@Test void a02_aboutNodeExists() throws Exception {
+		assertPage(page(client(), "/console/store"))
+			.hasNavPath("about")
+			.hasNavHref("about", "/console/about");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - routes
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_devRootRedirectsToFirstChild() throws Exception {
+		rawClient().get("/console/dev").run().assertStatus(303).assertHeader("Location").isMatches("*/console/dev/cards");
+	}
+
+	@Test void b02_levelThreePageActivatesFullPath() throws Exception {
+		assertPage(page(client(), "/console/dev/flavors/mustache")).isValid().hasActiveNav("dev", "flavors", "mustache");
+	}
+
+	@Test void b03_unknownFlavor404() throws Exception {
+		client().get("/console/dev/flavors/velocity").accept("text/html").run().assertStatus(404);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/SecureRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/SecureRest_Test.java
new file mode 100644
index 0000000000..8d416833cb
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/SecureRest_Test.java
@@ -0,0 +1,99 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import static org.apache.juneau.petstore.console.PetstoreConsoleFixture.*;
+import static org.apache.juneau.rest.server.console.test.PageContractAssert.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.petstore.console.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * P13: the open Secure page and the bearer-guarded API under it.
+ */
+@SuppressWarnings({
+	"resource" // MockRestClient is a no-op close.
+})
+class SecureRest_Test extends TestBase {
+
+	private static final String API = "/console/dev/secure/api";
+
+	private static MockRestClient json() {
+		return MockRestClient.create(PetstoreConsoleFixture.Host.class).noTrace().ignoreErrors().header("Accept", "application/json").build();
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - page
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_pageIsOpenAndRendersTheDemo() throws Exception {
+		var html = page(client(), "/console/dev/secure");
+		assertPage(html).isValid().hasActiveNav("dev", "secure").hasCardOrder("about", "try");
+		assertContainsAll(html,
+			"data-secure-call=\"none\"", "data-secure-call=\"petstore-user\"", "data-secure-call=\"wrong-token\"",
+			"id=\"secure-result\"", "/console/dev/secure/secure.js", "/console/dev/secure/api/whoami");
+	}
+
+	@Test void a02_scriptIsServed() throws Exception {
+		client().get("/console/dev/secure/secure.js").run()
+			.assertStatus(200)
+			.assertHeader("Content-Type").isContains("javascript")
+			.assertContent().isContains("data-secure-call", "Authorization");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - guarded API
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_noToken401WithChallenge() throws Exception {
+		json().get(API + "/pets").run()
+			.assertStatus(401)
+			.assertHeader("WWW-Authenticate").isContains("Bearer", "realm=\"petstore\"");
+	}
+
+	@Test void b02_demoToken200() throws Exception {
+		json().get(API + "/pets").header("Authorization", "Bearer petstore-user").run()
+			.assertStatus(200).assertContent().isContains("Mr. Frisky");
+	}
+
+	@Test void b03_wrongToken401() throws Exception {
+		json().get(API + "/pets").header("Authorization", "Bearer wrong-token").run().assertStatus(401);
+	}
+
+	@Test void b04_whoamiNamesThePrincipal() throws Exception {
+		var r = new JsonMap(json().get(API + "/whoami").header("Authorization", "Bearer petstore-admin").run()
+			.assertStatus(200).getContent().asString());
+		assertBean(r, "name", "admin");
+	}
+
+	@Test void b05_petByIdAndUnknown404() throws Exception {
+		var c = json();
+		assertBean(new JsonMap(c.get(API + "/pets/1").header("Authorization", "Bearer petstore-user").run()
+			.assertStatus(200).getContent().asString()), "id,name", "1,Mr. Frisky");
+		c.get(API + "/pets/99999").header("Authorization", "Bearer petstore-user").run()
+			.assertStatus(404).assertContent().isContains("Unknown pet '99999'");
+	}
+
+	@Test void b06_unguardedSiblingsStayOpen() throws Exception {
+		json().get("/petstore/pets/1").run().assertStatus(200);
+		json().get("/console/dev/themes").accept("text/html").run().assertStatus(200);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/ThemesRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/ThemesRest_Test.java
new file mode 100644
index 0000000000..ed165f27ed
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/ThemesRest_Test.java
@@ -0,0 +1,90 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev;
+
+import static org.apache.juneau.petstore.console.PetstoreConsoleFixture.*;
+import static org.apache.juneau.rest.server.console.test.PageContractAssert.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.server.console.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
+
+/**
+ * P9: the themes page, each stock theme via {@code ?theme=}, and the custom token theme.
+ */
+@SuppressWarnings({
+	"resource" // MockRestClient is a no-op close.
+})
+class ThemesRest_Test extends TestBase {
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - stock themes
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_defaultIsOpen() throws Exception {
+		var html = page(client(), "/console/dev/themes");
+		assertPage(html).isValid().hasActiveNav("dev", "themes").hasCardOrder("swatches", "picker", "authoring").hasTheme("open");
+	}
+
+	@ParameterizedTest
+	@ValueSource(strings={"open", "light-red", "light-brown", "red", "gray"})
+	void a02_eachStockTheme(String name) throws Exception {
+		var html = page(client(), "/console/dev/themes?theme=" + name);
+		assertPage(html).isValid().hasTheme(name);
+		assertContains("juneau-theme-" + name + ".css", html);
+	}
+
+	@Test void a03_pickerListsEveryThemeAndCustom() throws Exception {
+		var html = page(client(), "/console/dev/themes");
+		for (var n : ConsoleChromeMixin.BUILTIN_THEME_NAMES)
+			assertContains("href=\"/console/dev/themes?theme=" + n + "\"", html);
+		assertContains("href=\"/console/dev/themes?theme=custom\"", html);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - custom token theme
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_customThemeEmitsTheOverrideBlock() throws Exception {
+		var html = page(client(), "/console/dev/themes?theme=custom");
+		assertPage(html).isValid().hasTheme("light-brown");
+		assertContainsAll(html, "juneau-theme-light-brown.css", "--jc-pill-red-bg:#fdeceb", "--jc-tab-bar-bg:var(--jc-card-bg)");
+	}
+
+	@Test void b02_stockThemeHasNoOverrideBlock() throws Exception {
+		var html = page(client(), "/console/dev/themes?theme=light-brown");
+		assertFalse(html.contains("--jc-pill-red-bg:#fdeceb"), () -> "stock theme should not carry the custom overrides");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// c - validation and scope
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void c01_unknownThemeIs400() throws Exception {
+		client().get("/console/dev/themes?theme=neon").accept("application/json").run()
+			.assertStatus(400)
+			.assertContent().isContains("Unknown theme 'neon'", "'open, light-red, light-brown, red, gray, custom'");
+	}
+
+	@Test void c02_otherPagesKeepTheDefault() throws Exception {
+		assertPage(page(client(), "/console/store")).hasTheme("open");
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/flavors/FlavorsRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/flavors/FlavorsRest_Test.java
new file mode 100644
index 0000000000..c4eb06bc35
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/dev/flavors/FlavorsRest_Test.java
@@ -0,0 +1,103 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.console.dev.flavors;
+
+import static org.apache.juneau.petstore.console.PetstoreConsoleFixture.*;
+import static org.apache.juneau.rest.server.console.test.PageContractAssert.*;
+import static org.apache.juneau.test.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.regex.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
+
+/**
+ * Rendering flavors: every flavor has a console page, and the three server flavors render the same pets as fragments.
+ */
+@SuppressWarnings({
+	"resource" // MockRestClient is a no-op close.
+})
+class FlavorsRest_Test extends TestBase {
+
+	private static final Pattern CELL = Pattern.compile("<td>([^<]*)</td>");
+
+	private static String fragment(String engine) throws Exception {
+		return client().get("/console/dev/flavors/" + engine + "/fragment").run().assertStatus(200).getContent().asString();
+	}
+
+	private static List<String> cells(String html) {
+		var out = new ArrayList<String>();
+		var m = CELL.matcher(html);
+		while (m.find())
+			out.add(m.group(1).trim());
+		return out;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - pages
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@ParameterizedTest(name="{0}")
+	@ValueSource(strings={"html", "freemarker", "mustache"})
+	void a01_serverFlavorPage(String engine) throws Exception {
+		var html = page(client(), "/console/dev/flavors/" + engine);
+		assertPage(html).isValid().hasActiveNav("dev", "flavors", engine).hasCardOrder("caption", "flavor").hasCard("flavor", "html");
+		assertContainsAll(html, "/console/dev/flavors/" + engine + "/fragment", "View source", Flavor.of(engine).caption());
+	}
+
+	@Test void a02_reactPageIframesTheApp() throws Exception {
+		var html = page(client(), "/console/dev/flavors/react");
+		assertPage(html).isValid().hasActiveNav("dev", "flavors", "react").hasCardOrder("caption", "flavor").hasCard("flavor", "html");
+		assertContainsAll(html, "<iframe", "src=\"/petstore-ui\"", "title=\"Petstore React app\"");
+	}
+
+	@Test void a03_flavorsRootRedirectsToHtml() throws Exception {
+		rawClient().get("/console/dev/flavors").run().assertStatus(303).assertHeader("Location").isMatches("*/console/dev/flavors/html");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - fragments
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@ParameterizedTest(name="{0}")
+	@ValueSource(strings={"html", "freemarker", "mustache"})
+	void b01_fragmentHasNoChrome(String engine) throws Exception {
+		var html = fragment(engine);
+		assertContainsAll(html, "<table", "data-flavor=\"" + engine + "\"", "Mr. Frisky");
+		for (var chrome : List.of("<html", "<body", "juneau-page", "<script"))
+			assertFalse(html.contains(chrome), () -> "fragment carries page chrome '" + chrome + "': " + html);
+	}
+
+	@Test void b02_allThreeFlavorsAgree() throws Exception {
+		var html = cells(fragment("html"));
+		assertSize(40, html);  // 10 pets x name, species, price, status
+		assertList(cells(fragment("freemarker")), html.toArray());
+		assertList(cells(fragment("mustache")), html.toArray());
+	}
+
+	@Test void b03_firstRowIsTheFirstSeededPet() throws Exception {
+		assertList(cells(fragment("html")).subList(0, 4), "Mr. Frisky", "CAT", "39.99", "AVAILABLE");
+	}
+
+	@Test void b04_reactHasNoFragment() throws Exception {
+		client().get("/console/dev/flavors/react/fragment").accept("text/html").run().assertStatus(404);
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/AuditRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/AuditRest_Test.java
index 703f259081..7912d6a8ca 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/AuditRest_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/AuditRest_Test.java
@@ -67,4 +67,21 @@ class AuditRest_Test extends TestBase {
 		var list = new JsonList(client().get("/console/ops/audit/rows").run().assertStatus(200).getContent().asString());
 		assertTrue(list.getMap(0).getString("at").compareTo(list.getMap(list.size() - 1).getString("at")) >= 0, "newest first");
 	}
+
+	private static JsonMap globalSearch(String value) throws Exception {
+		var body = "{\"draw\":1,\"length\":25,\"search\":{\"value\":\"" + value + "\"},\"columns\":["
+			+ "{\"data\":\"at\"},{\"data\":\"actor\"},{\"data\":\"action\"},{\"data\":\"entity\"},{\"data\":\"entityId\"},{\"data\":\"detail\"}]}";
+		return new JsonMap(client().post("/console/ops/audit/query", body).contentType("application/json").run().assertStatus(200).getContent().asString());
+	}
+
+	@Test void a05_globalSearchFindsSeededEntitiesAndOnlyRestockJobEntries() throws Exception {
+		// The browser test searches the audit table; the seed has Pet/Order/User entries.
+		var hit = globalSearch("Order");
+		assertTrue(hit.getInt("recordsFiltered") > 0, "seed has Order entries");
+		for (var row : hit.getList("data").elements(JsonMap.class))
+			assertString("Order", row.getString("entity"));
+		// The store is shared with JobsRest_Test, so restock jobs may already have run; no seed entry may match.
+		for (var row : globalSearch("restock").getList("data").elements(JsonMap.class))
+			assertTrue(row.getString("actor").startsWith("job:restock"), row.toString());
+	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/JobsRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/JobsRest_Test.java
index 2afc3ed37a..09f654a92b 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/JobsRest_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/ops/JobsRest_Test.java
@@ -26,6 +26,7 @@ import org.apache.juneau.rest.mock.classic.*;
 import org.junit.jupiter.api.*;
 
 @SuppressWarnings({
+	"java:S2925", // Polls an asynchronous job; the REST API has no completion hook to wait on.
 	"resource" // MockRestClient is a no-op close.
 })
 class JobsRest_Test extends TestBase {
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/pets/PetsRest_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/pets/PetsRest_Test.java
index d3e62671b4..d55d4cdcf1 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/pets/PetsRest_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/console/pets/PetsRest_Test.java
@@ -52,7 +52,7 @@ class PetsRest_Test extends TestBase {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// a - server-mode query (G-BQ6a)
+	// a - server-mode query
 	//-----------------------------------------------------------------------------------------------------------------
 
 	@Test void a01_queryPagesAndCounts() throws Exception {
@@ -142,7 +142,7 @@ class PetsRest_Test extends TestBase {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// c - stage (R11) and the P4 JSON feeds
+	// c - stage (inline edit) and the P4 JSON feeds
 	//-----------------------------------------------------------------------------------------------------------------
 
 	private static long create(MockRestClient c, String name) throws Exception {
@@ -218,7 +218,7 @@ class PetsRest_Test extends TestBase {
 		var id = json(c.post("/console/pets", "{\"name\":\"Keeper\",\"species\":\"CAT\",\"price\":3,\"tags\":[\"a\"],\"photo\":\"p\"}").contentType("application/json").run().getContent().asString()).getMap("row").getLong("id");
 		c.put("/console/pets/" + id, "{\"name\":\"Keeper2\",\"price\":4}").contentType("application/json").run().assertStatus(200);
 		var pet = json(c.get("/petstore/pets/" + id).run().getContent().asString());
-		assertBean(pet, "name,species,price,photo", "Keeper2,CAT,4.0,p");
+		assertBean(pet, "name,species,price,photo,status", "Keeper2,CAT,4.0,p,AVAILABLE");
 		assertList(pet.getList("tags"), "a");
 	}
 
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/dto/Pet_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/dto/Pet_Test.java
new file mode 100644
index 0000000000..3596cad8e8
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/dto/Pet_Test.java
@@ -0,0 +1,64 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.dto;
+
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+
+class Pet_Test extends TestBase {
+
+	@Test void a01_defaultStatusSetsWhenNull() {
+		assertBean(new Pet().defaultStatus(PetStatus.SOLD), "status", "SOLD");
+	}
+
+	@Test void a02_defaultStatusKeepsExisting() {
+		assertBean(new Pet().setStatus(PetStatus.AVAILABLE).defaultStatus(PetStatus.SOLD), "status", "AVAILABLE");
+	}
+
+	@Test void a03_defaultSpeciesSetsWhenNull() {
+		assertBean(new Pet().defaultSpecies(Species.CAT), "species", "CAT");
+	}
+
+	@Test void a04_defaultSpeciesKeepsExisting() {
+		assertBean(new Pet().setSpecies(Species.DOG).defaultSpecies(Species.CAT), "species", "DOG");
+	}
+
+	@Test void a05_defaultTagsSetsWhenNull() {
+		assertList(new Pet().defaultTags(List.of("a", "b")).getTags(), "a", "b");
+	}
+
+	@Test void a06_defaultTagsKeepsExisting() {
+		assertList(new Pet().setTags(List.of("x")).defaultTags(List.of("a", "b")).getTags(), "x");
+	}
+
+	@Test void a07_defaultPhotoSetsWhenNull() {
+		assertBean(new Pet().defaultPhoto("p"), "photo", "p");
+	}
+
+	@Test void a08_defaultPhotoKeepsExisting() {
+		assertBean(new Pet().setPhoto("old").defaultPhoto("p"), "photo", "old");
+	}
+
+	@Test void a09_defaultsChain() {
+		var p = new Pet().setStatus(PetStatus.SOLD).defaultStatus(PetStatus.AVAILABLE).defaultSpecies(Species.BIRD).defaultPhoto("p");
+		assertBean(p, "status,species,photo", "SOLD,BIRD,p");
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetFreemarkerViewResource_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetFreemarkerViewResource_Test.java
deleted file mode 100644
index fbe00d15d4..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetFreemarkerViewResource_Test.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import org.apache.juneau.*;
-import org.apache.juneau.rest.mock.classic.*;
-import org.junit.jupiter.api.*;
-
-@SuppressWarnings({
-	"resource" // Closeable test fixtures held in static fields; lifecycle managed by the test/framework, not a real leak.
-})
-class PetFreemarkerViewResource_Test extends TestBase {
-
-	private static final MockRestClient CLIENT = MockRestClient.buildLax(PetFreemarkerViewResource.class);
-
-	@Test void a01_viewPet_rendersTemplateWithPetName() throws Exception {
-		CLIENT.get("/pets/1/view")
-			.run()
-			.assertStatus(200)
-			.assertContent().asString().isContains("Mr. Frisky")
-			.assertContent().asString().isContains("Rendered via FreeMarker.");
-	}
-
-	@Test void a02_viewPet_rendersSpeciesAndStatus() throws Exception {
-		CLIENT.get("/pets/1/view")
-			.run()
-			.assertStatus(200)
-			.assertContent().asString().isContains("CAT")
-			.assertContent().asString().isContains("AVAILABLE");
-	}
-
-	@Test void a03_viewPet_unknownId_404() throws Exception {
-		CLIENT.get("/pets/99999/view")
-			.run()
-			.assertStatus(404);
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetHtmlResource_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetHtmlResource_Test.java
deleted file mode 100644
index adc8af458e..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetHtmlResource_Test.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.rest.mock.classic.*;
-import org.junit.jupiter.api.*;
-
-@SuppressWarnings({
-	"resource" // Test helpers return Closeables; Eclipse JDT @Owning warning is by design.
-})
-class PetHtmlResource_Test extends TestBase {
-
-	private static MockRestClient client() {
-		return MockRestClient.buildJsonLax(PetHtmlResource.class);
-	}
-
-	@Test void a01_getChildDescriptions_listsCardAndTable() throws Exception {
-		var content = client().get("/").run().assertStatus(200).getContent().asString();
-		assertTrue(content.contains("table"));
-		assertTrue(content.contains("card/1"));
-	}
-
-	@Test void a02_getPetCard_rendersSeededPet() throws Exception {
-		var content = client().get("/card/1").run().assertStatus(200).getContent().asString();
-		assertTrue(content.contains("Mr. Frisky"));
-		assertTrue(content.contains("CAT"));
-	}
-
-	@Test void a03_getPetCard_unknownId_404() throws Exception {
-		client().get("/card/99999").run().assertStatus(404);
-	}
-
-	@Test void a04_getPetTable_rendersAllSeededPets() throws Exception {
-		var content = client().get("/table").run().assertStatus(200).getContent().asString();
-		assertTrue(content.contains("Name"));
-		assertTrue(content.contains("Mr. Frisky"));
-		assertTrue(content.contains("Kibbles"));
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetInfoResource_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetInfoResource_Test.java
deleted file mode 100644
index 844dd756a3..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetInfoResource_Test.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.petstore.dto.*;
-import org.apache.juneau.rest.mock.classic.*;
-import org.junit.jupiter.api.*;
-
-@SuppressWarnings({
-	"resource" // Test helpers return Closeables; Eclipse JDT @Owning warning is by design.
-})
-class PetInfoResource_Test extends TestBase {
-
-	private static MockRestClient client() {
-		return MockRestClient.buildJsonLax(PetInfoResource.class);
-	}
-
-	@Test void a01_getChildDescriptions_listsAllThree() throws Exception {
-		var content = client().get("/").run().assertStatus(200).getContent().asString();
-		assertTrue(content.contains("BeanDescription"));
-		assertTrue(content.contains("Hyperlink"));
-		assertTrue(content.contains("SeeOtherRoot"));
-	}
-
-	@Test void a02_getPetBeanDescription_describesPet() throws Exception {
-		var content = client().get("/BeanDescription").run().assertStatus(200).getContent().asString();
-		assertTrue(content.contains(Pet.class.getName()));
-		assertTrue(content.contains("species"));
-		assertTrue(content.contains("photo"));
-	}
-
-	@Test void a03_getHyperlink_pointsAtSelf() throws Exception {
-		var content = client().get("/Hyperlink").run().assertStatus(200).getContent().asString();
-		assertTrue(content.contains("/petstore-info"));
-	}
-
-	@Test void a04_getSeeOtherRoot_redirectsToRoot() throws Exception {
-		var noRedirect = MockRestClient.create(PetInfoResource.class).disableRedirectHandling().ignoreErrors().build();
-		noRedirect.get("/SeeOtherRoot").run().assertStatus(303);
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetMustacheViewResource_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetMustacheViewResource_Test.java
deleted file mode 100644
index d0b074e3b5..0000000000
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetMustacheViewResource_Test.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.petstore.rest;
-
-import org.apache.juneau.*;
-import org.apache.juneau.rest.mock.classic.*;
-import org.junit.jupiter.api.*;
-
-@SuppressWarnings({
-	"resource" // Closeable test fixtures held in static fields; lifecycle managed by the test/framework, not a real leak.
-})
-class PetMustacheViewResource_Test extends TestBase {
-
-	private static final MockRestClient CLIENT = MockRestClient.buildLax(PetMustacheViewResource.class);
-
-	@Test void a01_viewPet_rendersTemplateWithPetName() throws Exception {
-		CLIENT.get("/pets/1/view")
-			.run()
-			.assertStatus(200)
-			.assertContent().asString().isContains("Mr. Frisky")
-			.assertContent().asString().isContains("Rendered via Mustache.");
-	}
-
-	@Test void a02_viewPet_rendersSpeciesAndStatus() throws Exception {
-		CLIENT.get("/pets/1/view")
-			.run()
-			.assertStatus(200)
-			.assertContent().asString().isContains("CAT")
-			.assertContent().asString().isContains("AVAILABLE");
-	}
-
-	@Test void a03_viewPet_unknownId_404() throws Exception {
-		CLIENT.get("/pets/99999/view")
-			.run()
-			.assertStatus(404);
-	}
-}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetstoreUiResource_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetstoreUiResource_Test.java
index 3045b47c63..68bb1a2c98 100644
--- a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetstoreUiResource_Test.java
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/rest/PetstoreUiResource_Test.java
@@ -48,4 +48,12 @@ class PetstoreUiResource_Test extends TestBase {
 			.run()
 			.assertStatus(404);
 	}
+
+	@Test void a04_resourceRoot_servesTheApp() throws Exception {
+		// The console iframes and links the bare /petstore-ui URL; it must answer, not 404.
+		CLIENT.get("/")
+			.run()
+			.assertStatus(200)
+			.assertContent().asString().isContains("<title>Juneau Petstore — React UI</title>");
+	}
 }
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/sql/PetstoreSqlSchema.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/sql/PetstoreSqlSchema.java
new file mode 100644
index 0000000000..64b2f90d79
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/sql/PetstoreSqlSchema.java
@@ -0,0 +1,142 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.sql;
+
+import static org.apache.juneau.commons.beanquery.SearchType.*;
+
+import java.sql.*;
+import java.time.*;
+
+import org.apache.juneau.beanquery.postgres.*;
+import org.apache.juneau.beanquery.sql.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.petstore.service.*;
+
+/**
+ * H2 tables holding a copy of a seeded {@link PetStore}, and the SQL query contexts over them.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	<jk>var</jk> <jv>schema</jv> = PetstoreSqlSchema.<jsm>load</jsm>(<js>"jdbc:h2:mem:petstore;MODE=PostgreSQL;DB_CLOSE_DELAY=-1"</js>, <jv>store</jv>);
+ * 	<jk>try</jk> (<jk>var</jk> <jv>s</jv> = <jv>schema</jv>.pets().getSession()) {
+ * 		<jk>var</jk> <jv>page</jv> = DataTablesQuery.<jsm>run</jsm>(<jv>request</jv>, <jv>s</jv>);
+ * 	}
+ * </p>
+ */
+@SuppressWarnings({
+	"resource" // connect() returns a Connection owned by the caller (closed via try-with-resources); Eclipse JDT @Owning warning is by design.
+})
+public final class PetstoreSqlSchema {
+
+	private final String url;
+
+	private PetstoreSqlSchema(String url) {
+		this.url = url;
+	}
+
+	/**
+	 * Creates the tables and copies every pet and audit row from the store.
+	 *
+	 * @param url The H2 URL; must keep the database open between connections ({@code DB_CLOSE_DELAY=-1}).
+	 * @param store The seeded store.
+	 * @return The schema.
+	 * @throws SQLException On a DDL or insert failure.
+	 */
+	public static PetstoreSqlSchema load(String url, PetStore store) throws SQLException {
+		try (var c = DriverManager.getConnection(url); var s = c.createStatement()) {
+			s.execute("CREATE TABLE \"pet\" (\"id\" BIGINT PRIMARY KEY, \"name\" VARCHAR(200), \"species\" VARCHAR(20), \"price\" REAL, \"status\" VARCHAR(20))");
+			s.execute("CREATE TABLE \"audit\" (\"id\" BIGINT PRIMARY KEY, \"at\" TIMESTAMP WITH TIME ZONE, \"actor\" VARCHAR(200), \"action\" VARCHAR(50), \"entity\" VARCHAR(50), \"entityId\" VARCHAR(50), \"detail\" VARCHAR(2000))");
+			try (var p = c.prepareStatement("INSERT INTO \"pet\" VALUES (?, ?, ?, ?, ?)")) {
+				for (var x : store.getPets()) {
+					p.setLong(1, x.getId());
+					p.setString(2, x.getName());
+					p.setString(3, x.getSpecies() == null ? null : x.getSpecies().name());
+					p.setFloat(4, x.getPrice());
+					p.setString(5, x.getStatus() == null ? null : x.getStatus().name());
+					p.addBatch();
+				}
+				p.executeBatch();
+			}
+			try (var p = c.prepareStatement("INSERT INTO \"audit\" VALUES (?, ?, ?, ?, ?, ?, ?)")) {
+				for (var x : store.getAudit()) {
+					p.setLong(1, x.getId());
+					p.setObject(2, x.getAt().atOffset(ZoneOffset.UTC));
+					p.setString(3, x.getActor());
+					p.setString(4, x.getAction());
+					p.setString(5, x.getEntity());
+					p.setString(6, x.getEntityId());
+					p.setString(7, x.getDetail());
+					p.addBatch();
+				}
+				p.executeBatch();
+			}
+		}
+		return new PetstoreSqlSchema(url);
+	}
+
+	private Connection connect() {
+		try {
+			return DriverManager.getConnection(url);
+		} catch (SQLException e) {
+			throw new IllegalStateException("Cannot connect to '" + url + "'", e);
+		}
+	}
+
+	/** @return A query context over the {@code pet} table, with the same columns as {@code PetStore.PET_QUERY}. */
+	public SqlBeanQueryContext<Pet> pets() {
+		return SqlBeanQueryContext.create(Pet.class)
+			.dialect(PostgresDialect.INSTANCE)
+			.table("pet")
+			.column("id", NUMERIC)
+			.column("name", TEXT)
+			.column("species", ENUM)
+			.column("price", NUMERIC)
+			.column("status", ENUM)
+			.rowMapper((rs, cols) -> new Pet()
+				.setId(rs.getLong("id"))
+				.setName(rs.getString("name"))
+				.setSpecies(rs.getString("species") == null ? null : Species.valueOf(rs.getString("species")))
+				.setPrice(rs.getFloat("price"))
+				.setStatus(rs.getString("status") == null ? null : PetStatus.valueOf(rs.getString("status"))))
+			.connectionSupplier(this::connect)
+			.build();
+	}
+
+	/** @return A query context over the {@code audit} table, with the same columns as {@code PetStore.AUDIT_QUERY}. */
+	public SqlBeanQueryContext<AuditEntry> audit() {
+		return SqlBeanQueryContext.create(AuditEntry.class)
+			.dialect(PostgresDialect.INSTANCE)
+			.table("audit")
+			.column("id", NUMERIC)
+			.column("at", TIMESTAMP)
+			.column("actor", TEXT)
+			.column("action", TEXT)
+			.column("entity", TEXT)
+			.column("entityId", TEXT)
+			.column("detail", TEXT)
+			.rowMapper((rs, cols) -> new AuditEntry()
+				.setId(rs.getLong("id"))
+				.setAt(rs.getObject("at", OffsetDateTime.class).toInstant())
+				.setActor(rs.getString("actor"))
+				.setAction(rs.getString("action"))
+				.setEntity(rs.getString("entity"))
+				.setEntityId(rs.getString("entityId"))
+				.setDetail(rs.getString("detail")))
+			.connectionSupplier(this::connect)
+			.build();
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/sql/PetstoreSql_Test.java b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/sql/PetstoreSql_Test.java
new file mode 100644
index 0000000000..be8384eb99
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/java/org/apache/juneau/petstore/sql/PetstoreSql_Test.java
@@ -0,0 +1,129 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.petstore.sql;
+
+import static org.apache.juneau.test.bct.BctAssertions.*;
+
+import java.util.*;
+import java.util.function.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.beanquery.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.petstore.console.data.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.petstore.service.*;
+import org.apache.juneau.rest.server.datatables.*;
+import org.apache.juneau.rest.server.datatables.adapter.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
+
+/**
+ * SQL variant: the same DataTables requests return the same page in memory and over H2.
+ */
+@SuppressWarnings({
+	"resource" // Each query session is closed by page()'s try-with-resources; Eclipse JDT @Owning warning is by design.
+})
+class PetstoreSql_Test extends TestBase {
+
+	private static final String PET_COLUMNS = "[{\"data\":\"id\"},{\"data\":\"name\"},{\"data\":\"species\"},{\"data\":\"price\"},{\"data\":\"status\"}]";
+	private static final String AUDIT_COLUMNS = "[{\"data\":\"at\"},{\"data\":\"actor\"},{\"data\":\"action\"},{\"data\":\"entity\"},{\"data\":\"entityId\"},{\"data\":\"detail\"}]";
+
+	private static PetStore store;
+	private static PetstoreSqlSchema sql;
+
+	@BeforeAll
+	static void load() throws Exception {
+		store = PetstoreSeed.create().populate(new PetStore(PetstoreSeed.DEFAULT_CLOCK));
+		sql = PetstoreSqlSchema.load("jdbc:h2:mem:PetstoreSql_Test;MODE=PostgreSQL;DB_CLOSE_DELAY=-1", store);
+	}
+
+	private static DataTablesRequest request(String columns, String body) throws Exception {
+		return Json.to("{\"draw\":1,\"length\":10,\"columns\":" + columns + "," + body + "}", DataTablesRequest.class);
+	}
+
+	private static <T> List<Object> page(DataTablesRequest r, BeanQuerySession<T> s, Function<T,Object> id) {
+		try (s) {
+			var res = DataTablesQuery.run(r, s);
+			assertString("", Objects.toString(res.getError(), ""));  // the query error, if any
+			var out = new ArrayList<Object>();
+			out.add(res.getRecordsTotal());
+			out.add(res.getRecordsFiltered());
+			res.getData().forEach(x -> out.add(id.apply(x)));
+			return out;
+		}
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a — pets
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@ParameterizedTest(name="{0}")
+	@CsvSource(delimiter='|', textBlock="""
+		a01 default order       | "start":0,"order":[{"column":0,"dir":"asc"}]
+		a02 third page by price | "start":20,"order":[{"column":3,"dir":"desc"},{"column":0,"dir":"asc"}]
+		a03 global text search  | "start":0,"search":{"value":"frisky"},"order":[{"column":0,"dir":"asc"}]
+		a04 status ribbon $eq   | "start":0,"order":[{"column":0,"dir":"asc"}],"columns":[{"data":"id"},{"data":"name"},{"data":"species"},{"data":"price"},{"data":"status","search":{"value":"$eq(AVAILABLE)"}}]
+		a05 species ribbon $in  | "start":0,"order":[{"column":1,"dir":"asc"},{"column":0,"dir":"asc"}],"columns":[{"data":"id"},{"data":"name"},{"data":"species","search":{"value":"$in(DOG,CAT)"}},{"data":"price"},{"data":"status"}]
+		a06 merged search       | "start":0,"search":{"value":"a"},"order":[{"column":0,"dir":"asc"}],"columns":[{"data":"id"},{"data":"name"},{"data":"species"},{"data":"price"},{"data":"status","search":{"value":"$eq(SOLD)"}}]
+		a07 no match            | "start":0,"search":{"value":"zzzz-no-such-pet"},"order":[{"column":0,"dir":"asc"}]
+		""")
+	void a_petsMatch(String name, String body) throws Exception {
+		var r = request(PET_COLUMNS, body);
+		assertList(() -> "Case '" + name + "'", page(r, sql.pets().getSession(), Pet::getId), page(r, store.queryPets(), Pet::getId).toArray());
+	}
+
+	@Test void a08_defaultPageIsNotTrivial() throws Exception {
+		var p = page(request(PET_COLUMNS, "\"start\":0,\"order\":[{\"column\":0,\"dir\":\"asc\"}]"), sql.pets().getSession(), Pet::getId);
+		assertSize(12, p); // total, filtered, ten ids
+	}
+
+	@Test void a09_columnOverrideFilters() throws Exception {
+		// The ribbon cases rely on the body's later "columns" key replacing the default one; if it didn't, both
+		// sides would match unfiltered and the parity check would prove nothing.
+		var p = page(request(PET_COLUMNS, "\"start\":0,\"order\":[{\"column\":0,\"dir\":\"asc\"}],\"columns\":[{\"data\":\"id\"},{\"data\":\"name\"},{\"data\":\"species\"},{\"data\":\"price\"},{\"data\":\"status\",\"search\":{\"value\":\"$eq(SOLD)\"}}]"), sql.pets().getSession(), Pet::getId);
+		assertString("true", String.valueOf(((Number) p.get(1)).longValue() < ((Number) p.get(0)).longValue()));
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b — audit
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@ParameterizedTest(name="{0}")
+	@CsvSource(delimiter='|', textBlock="""
+		b01 newest first      | "start":0,"order":[{"column":0,"dir":"desc"}]
+		b02 second page       | "start":10,"order":[{"column":0,"dir":"desc"}]
+		b03 global search     | "start":0,"search":{"value":"pet"},"order":[{"column":0,"dir":"desc"}]
+		b04 actor column      | "start":0,"order":[{"column":0,"dir":"desc"}],"columns":[{"data":"at"},{"data":"actor","search":{"value":"console:"}},{"data":"action"},{"data":"entity"},{"data":"entityId"},{"data":"detail"}]
+		""")
+	void b_auditMatches(String name, String body) throws Exception {
+		var r = request(AUDIT_COLUMNS, body);
+		assertList(() -> "Case '" + name + "'", page(r, sql.audit().getSession(), AuditEntry::getId), page(r, store.queryAudit(), AuditEntry::getId).toArray());
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// c — schema parity
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void c01_sqlColumnTypesMatchInMemory() {
+		for (var c : PetStore.PET_QUERY.columns())
+			assertString(() -> "pet column '" + c + "'", String.valueOf(PetStore.PET_QUERY.getColumnType(c)), sql.pets().getColumnType(c));
+		for (var c : PetStore.AUDIT_QUERY.columns())
+			assertString(() -> "audit column '" + c + "'", String.valueOf(PetStore.AUDIT_QUERY.getColumnType(c)), sql.audit().getColumnType(c));
+	}
+}
diff --git a/juneau-petstore/juneau-petstore-core/src/test/js/petstore-browser.cjs b/juneau-petstore/juneau-petstore-core/src/test/js/petstore-browser.cjs
new file mode 100644
index 0000000000..4be7724c5b
--- /dev/null
+++ b/juneau-petstore/juneau-petstore-core/src/test/js/petstore-browser.cjs
@@ -0,0 +1,113 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+/*
+ * Petstore browser harness: node petstore-browser.cjs <cases.json>
+ *
+ * Input:  {baseUrl, snapshots, cases:[{name, path, actions?, queries?, fetches?, capture?, allowFailedLoads?}]}
+ * Output (stdout): {cases:{<name>:{status, jsFailures, deprecations, queries, fetches, requests, timeouts}}}
+ *
+ * Each action is one of:
+ *   {click: selector} {fill: [selector, text]} {press: [selector, key]}
+ *   {waitFor: jsExpression, timeoutMs?}  (records the expression in timeouts if it never becomes truthy)
+ *   {waitForCaptured: n, timeoutMs?}  (waits until n requests matching the case's capture were recorded)
+ *   {evaluate: jsExpression}  {screenshot: fileName}
+ *
+ * allowFailedLoads drops Chromium's own "Failed to load resource" console errors, which a deliberate 401/404
+ * fetch produces; the case then asserts the status itself.  Script errors are still recorded.
+ */
+const fs = require('node:fs');
+const path = require('node:path');
+const { chromium } = require('playwright');
+
+(async () => {
+	const input = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
+	const browser = await chromium.launch();
+	const out = {};
+	try {
+		for (const c of input.cases) {
+			out[c.name] = await runCase(browser, input, c);
+		}
+	} finally {
+		await browser.close();
+	}
+	process.stdout.write(JSON.stringify({ cases: out }));
+})().catch(e => {
+	process.stderr.write(String(e?.stack || e));
+	process.exit(1);
+});
+
+async function runCase(browser, input, c) {
+	const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
+	const page = await context.newPage();
+	const r = { status: 0, jsFailures: [], deprecations: [], queries: {}, fetches: {}, requests: [], timeouts: [] };
+	page.on('pageerror', e => r.jsFailures.push(String(e)));
+	page.on('console', m => {
+		const t = m.text();
+		if (m.type() === 'error') {
+			if (!(c.allowFailedLoads && /Failed to load resource/.test(t))) r.jsFailures.push(t);
+		}
+		else if (m.type() === 'warning' && /is deprecated/.test(t)) r.deprecations.push(t);
+	});
+	if (c.capture) {
+		page.on('request', q => {
+			if (q.url().includes(c.capture)) r.requests.push({ url: q.url(), method: q.method(), postData: q.postData() });
+		});
+	}
+	try {
+		const resp = await page.goto(input.baseUrl + c.path, { waitUntil: 'networkidle' });
+		r.status = resp ? resp.status() : 0;
+		for (const a of c.actions || []) await act(page, input, c, a, r);
+		for (const [k, expr] of Object.entries(c.queries || {})) {
+			r.queries[k] = await page.evaluate(expr);
+		}
+		for (const [k, url] of Object.entries(c.fetches || {})) {
+			r.fetches[k] = await page.evaluate(async u => {
+				const x = await fetch(u, { headers: { Accept: 'application/json' }, credentials: 'same-origin' });
+				return { status: x.status, body: await x.text() };
+			}, url);
+		}
+	} catch (e) {
+		r.jsFailures.push('harness: ' + String(e?.message || e));
+	} finally {
+		await context.close();
+	}
+	return r;
+}
+
+async function act(page, input, c, a, r) {
+	if (a.click) return page.click(a.click);
+	if (a.fill) return page.fill(a.fill[0], a.fill[1]);
+	if (a.press) return page.press(a.press[0], a.press[1]);
+	if (a.evaluate) return page.evaluate(a.evaluate);
+	if (a.screenshot) return page.screenshot({ path: path.join(input.snapshots, a.screenshot), fullPage: true });
+	if (a.waitFor) {
+		try {
+			await page.waitForFunction(a.waitFor, null, { timeout: a.timeoutMs || 10000 });
+		} catch { // NOSONAR javascript:S2486 -- a timeout is the expected failure here; it is recorded in r.timeouts
+			r.timeouts.push(a.waitFor);
+		}
+		return;
+	}
+	if (a.waitForCaptured) {
+		const deadline = Date.now() + (a.timeoutMs || 10000);
+		while (r.requests.length < a.waitForCaptured && Date.now() < deadline) await page.waitForTimeout(100);
+		if (r.requests.length < a.waitForCaptured) r.timeouts.push('captured ' + a.waitForCaptured + ' of ' + c.capture);
+		return;
+	}
+	throw new Error('Unknown action ' + JSON.stringify(a));
+}
diff --git a/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java b/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
index 280684e918..14ca77504b 100644
--- a/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
+++ b/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
@@ -46,7 +46,7 @@ import org.apache.juneau.rest.server.widget.*;
  * {@code .cfg}/{@code Config} equivalent.
  *
  * <p>
- * Registers the one seeded {@link PetStore} shared by every child resource (D-P6/D-P10).
+ * Registers the one seeded {@link PetStore} shared by every child resource.
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
@@ -66,13 +66,9 @@ import org.apache.juneau.rest.server.widget.*;
 	description="Apache Juneau petstore sample application running under Jetty/Microservice.",
 	children={
 		PetstoreConsoleResource.class,
+		ConsoleAssetsRest.class,
 		PetStoreResource.class,
-		PetSecureResource.class,
-		PetMustacheViewResource.class,
-		PetFreemarkerViewResource.class,
 		PetstoreUiResource.class,
-		PetInfoResource.class,
-		PetHtmlResource.class,
 		ConfigResource.class,
 		LogsResource.class,
 		ShutdownResource.class
@@ -128,7 +124,7 @@ public class RootResources extends BasicRestServletGroup {
 	}
 
 	/**
-	 * {@code GET /} goes to the console store page (D-P6).
+	 * {@code GET /} goes to the console store page.
 	 *
 	 * @param req The request.
 	 * @return A 303 to {@code /console/store}.
diff --git a/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java b/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
index 8447fbed3c..e4e4597c30 100644
--- a/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
+++ b/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
@@ -16,6 +16,7 @@
  */
 package org.apache.juneau.petstore.jetty;
 
+import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.net.*;
@@ -28,6 +29,8 @@ import org.apache.juneau.microservice.*;
 import org.apache.juneau.testing.*;
 import org.junit.jupiter.api.*;
 import org.junit.jupiter.api.extension.*;
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
 
 /**
  * Integration test that boots the petstore Jetty deployment on an ephemeral port and exercises the CRUD surface
@@ -150,6 +153,22 @@ class PetstoreJetty_Test extends TestBase {
 		assertTrue(body.contains("id=\"juneau-page\""), "expected the page contract element: " + body);
 	}
 
+	/**
+	 * The console pages link their chrome assets at context-root {@code /juneau-console/*} URLs and their views
+	 * toolkit assets page-relative; both must be served for the console to style and start in a browser.
+	 */
+	@ParameterizedTest(name="{0}")
+	@ValueSource(strings={
+		"/juneau-console/chrome.css",
+		"/juneau-console/themes/juneau-theme-open.css",
+		"/juneau-console/juneau-console.js",
+		"/console/store/juneau-views.js",
+		"/console/ops/audit/juneau-datatables.js"
+	})
+	void a03_consoleAssetIsServed(String path) throws Exception {
+		assertEquals(200, get(path, "*/*").statusCode(), path);
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// b — petstore CRUD over real HTTP, JSON
 	//-----------------------------------------------------------------------------------------------------------------
@@ -215,61 +234,78 @@ class PetstoreJetty_Test extends TestBase {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// d — view-engine demos (parity with springboot deployment — both inherit from core)
+	// d — rendering flavors (parity with springboot deployment — both inherit from core)
 	//-----------------------------------------------------------------------------------------------------------------
 
-	@Test void d01_mustacheView_rendersPet() throws Exception {
-		var resp = get("/pet-views/mustache/pets/1/view", "text/html");
+	@Test void d01_mustacheFlavorFragment() throws Exception {
+		var resp = get("/console/dev/flavors/mustache/fragment", "text/html");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
-		var ct = resp.headers().firstValue("Content-Type").orElse("");
-		assertTrue(ct.startsWith("text/html"), "Content-Type was: " + ct);
-		assertTrue(resp.body().contains("Mr. Frisky"), "expected pet name in mustache view: " + resp.body());
-		assertTrue(resp.body().contains("Rendered via Mustache."), "expected mustache marker: " + resp.body());
+		assertContainsAll(resp.body(), "data-flavor=\"mustache\"", "Mr. Frisky");
 	}
 
-	@Test void d02_freemarkerView_rendersPet() throws Exception {
-		var resp = get("/pet-views/freemarker/pets/1/view", "text/html");
+	@Test void d02_freemarkerFlavorFragment() throws Exception {
+		var resp = get("/console/dev/flavors/freemarker/fragment", "text/html");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
-		var ct = resp.headers().firstValue("Content-Type").orElse("");
-		assertTrue(ct.startsWith("text/html"), "Content-Type was: " + ct);
-		assertTrue(resp.body().contains("Mr. Frisky"), "expected pet name in freemarker view: " + resp.body());
-		assertTrue(resp.body().contains("Rendered via FreeMarker."), "expected freemarker marker: " + resp.body());
+		assertContainsAll(resp.body(), "data-flavor=\"freemarker\"", "Mr. Frisky");
+	}
+
+	@Test void d03_flavorPageRendersInConsole() throws Exception {
+		var resp = get("/console/dev/flavors/html", "text/html");
+		assertEquals(200, resp.statusCode(), "body: " + resp.body());
+		assertContainsAll(resp.body(), "/console/dev/flavors/html/fragment", "View source");
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// e — AuthFilterChain gating /petstore-secure/* (parity with springboot deployment)
+	// e — BearerTokenGuard gating /console/dev/secure/api/* (parity with springboot deployment)
 	//-----------------------------------------------------------------------------------------------------------------
 
 	@Test void e01_secureEndpoint_noAuth_401() throws Exception {
-		var resp = get("/petstore-secure/pets", "application/json");
+		var resp = get("/console/dev/secure/api/pets", "application/json");
 		assertEquals(401, resp.statusCode(), "body: " + resp.body());
 		var challenge = resp.headers().firstValue("WWW-Authenticate").orElse("");
 		assertTrue(challenge.contains("Bearer"), "WWW-Authenticate should advertise Bearer scheme: " + challenge);
+		assertContains("realm=\"petstore\"", challenge);
 	}
 
 	@Test void e02_secureEndpoint_validToken_200() throws Exception {
-		var resp = getWithAuth("/petstore-secure/pets", "application/json", "Bearer petstore-user");
+		var resp = getWithAuth("/console/dev/secure/api/pets", "application/json", "Bearer petstore-user");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
 		assertTrue(resp.body().contains("Mr. Frisky"), "body: " + resp.body());
 	}
 
 	@Test void e03_secureEndpoint_unknownToken_401() throws Exception {
-		var resp = getWithAuth("/petstore-secure/pets", "application/json", "Bearer wrong-token");
+		var resp = getWithAuth("/console/dev/secure/api/pets", "application/json", "Bearer wrong-token");
 		assertEquals(401, resp.statusCode(), "body: " + resp.body());
 	}
 
 	@Test void e04_secureEndpoint_whoami_returnsPrincipalName() throws Exception {
-		var resp = getWithAuth("/petstore-secure/whoami", "application/json", "Bearer petstore-admin");
+		var resp = getWithAuth("/console/dev/secure/api/whoami", "application/json", "Bearer petstore-admin");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
 		assertTrue(resp.body().contains("\"name\":\"admin\""), "body: " + resp.body());
 	}
 
 	@Test void e05_unsecuredEndpoint_stillOpen() throws Exception {
-		// /petstore/* is NOT in the chain pattern, so anonymous access still works.
+		// /petstore/* has no guard, so anonymous access still works.
 		var resp = get("/petstore/pets/1", "application/json");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
 	}
 
+	//-----------------------------------------------------------------------------------------------------------------
+	// f — retired one-off resources (folded into the console; parity with springboot g)
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@ParameterizedTest(name="{0}")
+	@ValueSource(strings={
+		"/pet-views/mustache/pets/1/view",
+		"/pet-views/freemarker/pets/1/view",
+		"/petstore-html/card/1",
+		"/petstore-secure/pets",
+		"/petstore-info/BeanDescription"
+	})
+	void f01_retiredPathIs404(String path) throws Exception {
+		assertEquals(404, get(path, "text/html").statusCode(), path);
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// helpers
 	//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-petstore/juneau-petstore-springboot/src/main/java/org/apache/juneau/petstore/springboot/RootResources.java b/juneau-petstore/juneau-petstore-springboot/src/main/java/org/apache/juneau/petstore/springboot/RootResources.java
index f5be3c6252..f13d853349 100644
--- a/juneau-petstore/juneau-petstore-springboot/src/main/java/org/apache/juneau/petstore/springboot/RootResources.java
+++ b/juneau-petstore/juneau-petstore-springboot/src/main/java/org/apache/juneau/petstore/springboot/RootResources.java
@@ -55,13 +55,9 @@ import org.apache.juneau.rest.server.widget.*;
 	description="Apache Juneau petstore sample application running under Spring Boot.",
 	children={
 		PetstoreConsoleResource.class,
+		ConsoleAssetsRest.class,
 		PetStoreResource.class,
-		PetSecureResource.class,
-		PetMustacheViewResource.class,
-		PetFreemarkerViewResource.class,
 		PetstoreUiResource.class,
-		PetInfoResource.class,
-		PetHtmlResource.class,
 		HelloResource.class
 	}
 )
@@ -110,7 +106,7 @@ public class RootResources extends BasicSpringRestServletGroup {
 	}
 
 	/**
-	 * {@code GET /} goes to the console store page (D-P6).
+	 * {@code GET /} goes to the console store page.
 	 *
 	 * @param req The request.
 	 * @return A 303 to {@code /console/store}.
diff --git a/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java b/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
index 2d703bdc99..b6004c2955 100644
--- a/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
+++ b/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
@@ -16,6 +16,7 @@
  */
 package org.apache.juneau.petstore.springboot;
 
+import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.io.*;
@@ -25,6 +26,8 @@ import java.net.http.HttpResponse.*;
 import java.time.*;
 
 import org.junit.jupiter.api.*;
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
 import org.springframework.boot.test.context.*;
 import org.springframework.boot.test.context.SpringBootTest.*;
 import org.springframework.boot.test.web.server.*;
@@ -171,6 +174,22 @@ class PetstoreSpringboot_Test {
 		assertTrue(resp.body().contains("id=\"juneau-page\""), "expected the page contract element: " + resp.body());
 	}
 
+	/**
+	 * The console pages link their chrome assets at context-root {@code /juneau-console/*} URLs and their views
+	 * toolkit assets page-relative; both must be served for the console to style and start in a browser.
+	 */
+	@ParameterizedTest(name="{0}")
+	@ValueSource(strings={
+		"/juneau-console/chrome.css",
+		"/juneau-console/themes/juneau-theme-open.css",
+		"/juneau-console/juneau-console.js",
+		"/console/store/juneau-views.js",
+		"/console/ops/audit/juneau-datatables.js"
+	})
+	void a03_consoleAssetIsServed(String path) throws Exception {
+		assertEquals(200, get(path, "*/*").statusCode(), path);
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// b — petstore CRUD over real HTTP
 	//-----------------------------------------------------------------------------------------------------------------
@@ -245,47 +264,52 @@ class PetstoreSpringboot_Test {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// e — view-engine demos (parity with jetty deployment — both inherit from core)
+	// e — rendering flavors (parity with jetty deployment — both inherit from core)
 	//-----------------------------------------------------------------------------------------------------------------
 
-	@Test void e01_mustacheView_rendersPet() throws Exception {
-		var resp = get("/pet-views/mustache/pets/1/view", "text/html");
+	@Test void e01_mustacheFlavorFragment() throws Exception {
+		var resp = get("/console/dev/flavors/mustache/fragment", "text/html");
+		assertEquals(200, resp.statusCode(), "body: " + resp.body());
+		assertContainsAll(resp.body(), "data-flavor=\"mustache\"", "Mr. Frisky");
+	}
+
+	@Test void e02_freemarkerFlavorFragment() throws Exception {
+		var resp = get("/console/dev/flavors/freemarker/fragment", "text/html");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
-		assertTrue(resp.body().contains("Mr. Frisky"), "expected pet name in mustache view: " + resp.body());
-		assertTrue(resp.body().contains("Rendered via Mustache."), "expected mustache marker: " + resp.body());
+		assertContainsAll(resp.body(), "data-flavor=\"freemarker\"", "Mr. Frisky");
 	}
 
-	@Test void e02_freemarkerView_rendersPet() throws Exception {
-		var resp = get("/pet-views/freemarker/pets/1/view", "text/html");
+	@Test void e03_flavorPageRendersInConsole() throws Exception {
+		var resp = get("/console/dev/flavors/html", "text/html");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
-		assertTrue(resp.body().contains("Mr. Frisky"), "expected pet name in freemarker view: " + resp.body());
-		assertTrue(resp.body().contains("Rendered via FreeMarker."), "expected freemarker marker: " + resp.body());
+		assertContainsAll(resp.body(), "/console/dev/flavors/html/fragment", "View source");
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// f — AuthFilterChain gating /petstore-secure/* (parity with jetty deployment)
+	// f — BearerTokenGuard gating /console/dev/secure/api/* (parity with jetty deployment)
 	//-----------------------------------------------------------------------------------------------------------------
 
 	@Test void f01_secureEndpoint_noAuth_401() throws Exception {
-		var resp = get("/petstore-secure/pets", "application/json");
+		var resp = get("/console/dev/secure/api/pets", "application/json");
 		assertEquals(401, resp.statusCode(), "body: " + resp.body());
 		var challenge = resp.headers().firstValue("WWW-Authenticate").orElse("");
 		assertTrue(challenge.contains("Bearer"), "WWW-Authenticate should advertise Bearer scheme: " + challenge);
+		assertContains("realm=\"petstore\"", challenge);
 	}
 
 	@Test void f02_secureEndpoint_validToken_200() throws Exception {
-		var resp = getWithAuth("/petstore-secure/pets", "application/json", "Bearer petstore-user");
+		var resp = getWithAuth("/console/dev/secure/api/pets", "application/json", "Bearer petstore-user");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
 		assertTrue(resp.body().contains("Mr. Frisky"), "body: " + resp.body());
 	}
 
 	@Test void f03_secureEndpoint_unknownToken_401() throws Exception {
-		var resp = getWithAuth("/petstore-secure/pets", "application/json", "Bearer wrong-token");
+		var resp = getWithAuth("/console/dev/secure/api/pets", "application/json", "Bearer wrong-token");
 		assertEquals(401, resp.statusCode(), "body: " + resp.body());
 	}
 
 	@Test void f04_secureEndpoint_whoami_returnsPrincipalName() throws Exception {
-		var resp = getWithAuth("/petstore-secure/whoami", "application/json", "Bearer petstore-admin");
+		var resp = getWithAuth("/console/dev/secure/api/whoami", "application/json", "Bearer petstore-admin");
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
 		assertTrue(resp.body().contains("\"name\":\"admin\""), "body: " + resp.body());
 	}
@@ -295,6 +319,22 @@ class PetstoreSpringboot_Test {
 		assertEquals(200, resp.statusCode(), "body: " + resp.body());
 	}
 
+	//-----------------------------------------------------------------------------------------------------------------
+	// g — retired one-off resources (parity with jetty f)
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@ParameterizedTest(name="{0}")
+	@ValueSource(strings={
+		"/pet-views/mustache/pets/1/view",
+		"/pet-views/freemarker/pets/1/view",
+		"/petstore-html/card/1",
+		"/petstore-secure/pets",
+		"/petstore-info/BeanDescription"
+	})
+	void g01_retiredPathIs404(String path) throws Exception {
+		assertEquals(404, get(path, "text/html").statusCode(), path);
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// helpers
 	//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
index 1d074b9fd6..27f6933618 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
@@ -185,7 +185,7 @@ public final class RemoteClient {
 			// the design's precedence table collapsed to the surfaces this engine actually exposes).
 			var allowPrivateUrls = meta.isAllowPrivateUrls() || client.isAllowPrivateUrls();
 
-			// Apply the call-time @Url parameter / declarative baseUrl override (computed per call;
+			// Apply the call-time @Url parameter / declarative baseUrl override (computed per call and
 			// never cached on the shared meta objects).  Path-token substitution still runs at request time so @Path params
 			// can fill tokens inside the resolved URL.
 			var effectivePath = resolveEffectiveUrl(methodMeta, method, args, fullPath, allowPrivateUrls);
diff --git a/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java b/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
index 856d7b460b..1649bff590 100644
--- a/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
+++ b/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
@@ -105,7 +105,7 @@ class RestClient_Negotiation_Test extends TestBase {
 	void a06_defaultAcceptExactOrder() throws Exception {
 		var pset = ParserSet.create().add(JsonParser.DEFAULT, JsonlParser.DEFAULT).build();
 		try (var c = RestClient.builder().parsers(pset).build()) {
-			assertEquals("application/json, text/json, application/jcs+json, application/jsonl, application/x-ndjson, text/jsonl", c.getDefaultAccept());
+			assertEquals("application/json, text/json, application/jcs+json, application/jsonl, application/jsonlines, application/x-ndjson, text/jsonl", c.getDefaultAccept());
 		}
 	}
 
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
index 43d7cc8a64..222815c1b3 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
@@ -56,8 +56,12 @@ public class ContentType extends HttpMediaTypeHeader {
 	public static final ContentType APPLICATION_JSON5 = new ContentType("application/json5");
 	/** Content-Type for {@code application/json5l} (Juneau {@code Json5lSerializer}/{@code Json5lParser}). */
 	public static final ContentType APPLICATION_JSON5L = new ContentType("application/json5l");
+	/** Content-Type for {@code application/json5lines} (alias for {@code application/json5l}). */
+	public static final ContentType APPLICATION_JSON5LINES = new ContentType("application/json5lines");
 	/** Content-Type for {@code application/jsonl} (Juneau {@code JsonlSerializer}/{@code JsonlParser}). */
 	public static final ContentType APPLICATION_JSONL = new ContentType("application/jsonl");
+	/** Content-Type for {@code application/jsonlines} (alias for {@code application/jsonl}; the type suggested by the JSON Lines spec). */
+	public static final ContentType APPLICATION_JSONLINES = new ContentType("application/jsonlines");
 	/** Content-Type for {@code application/json-patch+json} (RFC 6902). */
 	public static final ContentType APPLICATION_JSON_PATCH = new ContentType("application/json-patch+json");
 	/** Content-Type for {@code application/ld+json} (JSON-LD). */
diff --git a/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java b/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
index 915b58c0c5..343b57271a 100644
--- a/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
+++ b/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
@@ -73,7 +73,9 @@ class ContentType_Test extends TestBase {
 			Arguments.of("APPLICATION_JSON", ContentType.APPLICATION_JSON, "application/json"),
 			Arguments.of("APPLICATION_JSON5", ContentType.APPLICATION_JSON5, "application/json5"),
 			Arguments.of("APPLICATION_JSON5L", ContentType.APPLICATION_JSON5L, "application/json5l"),
+			Arguments.of("APPLICATION_JSON5LINES", ContentType.APPLICATION_JSON5LINES, "application/json5lines"),
 			Arguments.of("APPLICATION_JSONL", ContentType.APPLICATION_JSONL, "application/jsonl"),
+			Arguments.of("APPLICATION_JSONLINES", ContentType.APPLICATION_JSONLINES, "application/jsonlines"),
 			Arguments.of("APPLICATION_JSON_PATCH", ContentType.APPLICATION_JSON_PATCH, "application/json-patch+json"),
 			Arguments.of("APPLICATION_LD_JSON", ContentType.APPLICATION_LD_JSON, "application/ld+json"),
 			Arguments.of("APPLICATION_MANIFEST_JSON", ContentType.APPLICATION_MANIFEST_JSON, "application/manifest+json"),
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/AssetResolutionAssert.java b/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/AssetResolutionAssert.java
new file mode 100644
index 0000000000..bff179fe3f
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/AssetResolutionAssert.java
@@ -0,0 +1,155 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.console.test;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.regex.*;
+
+/**
+ * Test helper that checks every asset a rendered console page links (stylesheets, scripts, images, icons) actually
+ * resolves.
+ *
+ * <p>
+ * The console chrome links its assets at context-root URLs such as {@code /juneau-console/chrome.css}, so they only
+ * load if the host mounts something at those paths.  Page tests that assert the hrefs cannot see a missing mount;
+ * this helper fetches each URL through whatever client the caller supplies and reports every one that does not
+ * answer {@code 200}.
+ *
+ * <p>
+ * The fetcher is a plain {@link Function} from URL to HTTP status, so the class has no dependency on a test
+ * framework or on a particular client.  Failures throw {@link AssertionError}.  Only same-origin URLs are checked:
+ * absolute {@code http(s):}, protocol-relative, {@code data:} and fragment-only URLs are skipped.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * 	MockRestClient <jv>client</jv> = MockRestClient.<jsm>buildLax</jsm>(MyHost.<jk>class</jk>);
+ * 	String <jv>html</jv> = <jv>client</jv>.get(<js>"/page"</js>).run().getContent().asString();
+ * 	AssetResolutionAssert.<jsm>assertAssetsResolve</jsm>(<jv>html</jv>, <jv>url</jv> -&gt; <jv>client</jv>.get(<jv>url</jv>).run().getStatusCode());
+ * </p>
+ *
+ * @since 10.0.0
+ */
+public final class AssetResolutionAssert {
+
+	private static final Pattern TAG = Pattern.compile("<(script|img|link)\\b([^>]*)>", Pattern.CASE_INSENSITIVE);
+	private static final Pattern SRC = attr("src");
+	private static final Pattern HREF = attr("href");
+	private static final Pattern REL = attr("rel");
+	private static final Set<String> ASSET_RELS = Set.of("stylesheet", "icon", "shortcut", "apple-touch-icon", "preload", "modulepreload", "manifest");
+
+	private AssetResolutionAssert() {}
+
+	private static Pattern attr(String name) {
+		return Pattern.compile("(?<![\\w-])" + name + "\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')", Pattern.CASE_INSENSITIVE);
+	}
+
+	private static String value(Pattern p, String attrs) {
+		var m = p.matcher(attrs);
+		if (! m.find())
+			return null;
+		return m.group(1) != null ? m.group(1) : m.group(2);
+	}
+
+	private static boolean isAssetLink(String attrs) {
+		var rel = value(REL, attrs);
+		if (rel == null)
+			return false;
+		for (var token : rel.toLowerCase(Locale.ROOT).split("\\s+"))
+			if (ASSET_RELS.contains(token))
+				return true;
+		return false;
+	}
+
+	private static boolean isSameOrigin(String url) {
+		return ! (url.isEmpty() || url.startsWith("#") || url.startsWith("//") || url.matches("(?i)^[a-z][a-z0-9+.-]*:.*"));
+	}
+
+	/**
+	 * Returns the same-origin asset URLs a page links, in document order and without duplicates.
+	 *
+	 * <p>
+	 * Covers {@code <script src>}, {@code <img src>} and {@code <link href>} whose {@code rel} is a stylesheet,
+	 * icon, preload or manifest.  {@code &amp;} is decoded and any fragment is dropped.
+	 *
+	 * @param html The rendered page.
+	 * @return The asset URLs.  Never <jk>null</jk>.
+	 */
+	public static List<String> assetUrls(String html) {
+		var urls = new LinkedHashSet<String>();
+		var m = TAG.matcher(html);
+		while (m.find()) {
+			var attrs = m.group(2);
+			String url;
+			if ("link".equalsIgnoreCase(m.group(1)))
+				url = isAssetLink(attrs) ? value(HREF, attrs) : null;
+			else
+				url = value(SRC, attrs);
+			if (url == null)
+				continue;
+			url = url.replace("&amp;", "&").trim();
+			var hash = url.indexOf('#');
+			if (hash > 0)
+				url = url.substring(0, hash);
+			if (isSameOrigin(url))
+				urls.add(url);
+		}
+		return List.copyOf(urls);
+	}
+
+	/**
+	 * Returns a description of every linked asset that does not answer {@code 200}.
+	 *
+	 * @param html The rendered page.
+	 * @param statusOf Fetches a URL and returns its HTTP status code.
+	 * @return One {@code "<status> <url>"} entry per failing asset, sorted; empty when every asset resolves.
+	 */
+	public static List<String> unresolved(String html, Function<String,Integer> statusOf) {
+		var bad = new TreeSet<String>();
+		for (var url : assetUrls(html)) {
+			var status = statusOf.apply(url);
+			if (status == null || status != 200)
+				bad.add(status + " " + url);
+		}
+		return List.copyOf(bad);
+	}
+
+	/**
+	 * Asserts that every asset the page links answers {@code 200}.
+	 *
+	 * @param html The rendered page.
+	 * @param statusOf Fetches a URL and returns its HTTP status code.
+	 * @throws AssertionError If any linked asset does not resolve; the message lists each one.
+	 */
+	public static void assertAssetsResolve(String html, Function<String,Integer> statusOf) {
+		assertAssetsResolve(null, html, statusOf);
+	}
+
+	/**
+	 * Asserts that every asset the page links answers {@code 200}, naming the page in the failure message.
+	 *
+	 * @param page A label for the page (typically its path); can be <jk>null</jk>.
+	 * @param html The rendered page.
+	 * @param statusOf Fetches a URL and returns its HTTP status code.
+	 * @throws AssertionError If any linked asset does not resolve; the message lists each one.
+	 */
+	public static void assertAssetsResolve(String page, String html, Function<String,Integer> statusOf) {
+		var bad = unresolved(html, statusOf);
+		if (! bad.isEmpty())
+			throw new AssertionError((page == null ? "" : "Unresolved assets linked from " + page + ":\n") + String.join("\n", bad));
+	}
+}
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java b/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
index 059865e006..5cbc4e269d 100644
--- a/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
+++ b/juneau-rest/juneau-rest-server-console-ui/src/main/java/org/apache/juneau/rest/server/console/test/package-info.java
@@ -16,10 +16,11 @@
  */
 /**
  * Test helpers for console pages: {@link org.apache.juneau.rest.server.console.test.PageContractAssert} asserts on the
- * {@code #juneau-page} contract of a rendered page instead of on rendered chrome HTML.
+ * {@code #juneau-page} contract of a rendered page instead of on rendered chrome HTML, and
+ * {@link org.apache.juneau.rest.server.console.test.AssetResolutionAssert} asserts that every asset a page links resolves.
  *
  * <p>
- * Main scope on purpose (C1-D3): adopters already depend on {@code juneau-rest-server-console-ui}. The package has no
+ * Main scope on purpose: adopters already depend on {@code juneau-rest-server-console-ui}. The package has no
  * test-framework dependency; failures are plain {@link java.lang.AssertionError}s.
  *
  * @since 10.0.0
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/test/AssetResolutionAssert_Test.java b/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/test/AssetResolutionAssert_Test.java
new file mode 100644
index 0000000000..0ee36c91b8
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/test/AssetResolutionAssert_Test.java
@@ -0,0 +1,116 @@
+/*
+ * 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
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.console.test;
+
+import static org.apache.juneau.test.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.mock.classic.*;
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.console.*;
+import org.apache.juneau.rest.server.servlet.*;
+import org.junit.jupiter.api.*;
+
+@SuppressWarnings({
+	"resource" // The static in-process MockRestClient C (and its responses) live for the whole test class and hold no external resources
+})
+class AssetResolutionAssert_Test extends TestBase {
+
+	@Rest(mixins=ConsoleChromeMixin.class)
+	public static class Host extends BasicRestServlet {
+		private static final long serialVersionUID = 1L;
+
+		/** A representative console page: the chrome's own stylesheet, theme and shell script, plus an image. */
+		@RestGet(path="/page")
+		public void page(RestRequest req, RestResponse res) throws IOException {
+			res.setContentType("text/html");
+			res.getWriter().write("<html><head>"
+				+ "<link rel=\"stylesheet\" href=\"" + ConsoleChromeMixin.chromeCssUrl(req) + "\">"
+				+ "<link rel=\"stylesheet\" href=\"" + ConsoleChromeMixin.themeAssetUrl(req, "gray") + "\">"
+				+ "<script src=\"" + ConsoleChromeMixin.consoleJsUrl(req) + "\"></script>"
+				+ "</head><body><img src=\"/logo.svg\"></body></html>");
+		}
+
+		@RestGet(path="/logo.svg")
+		public void logo(RestResponse res) throws IOException {
+			res.setContentType("image/svg+xml");
+			res.getWriter().write("<svg xmlns=\"http://www.w3.org/2000/svg\"/>");
+		}
+	}
+
+	private static final MockRestClient C = MockRestClient.buildLax(Host.class);
+
+	private static int status(String url) {
+		try {
+			return C.get(url).run().getStatusCode();
+		} catch (Exception e) {
+			throw new IllegalStateException(e);
+		}
+	}
+
+	private static String html(String path) throws Exception {
+		return C.get(path).accept("text/html").run().assertStatus(200).getContent().asString();
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// a - the representative page
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_chromePageAssetsAllResolve() throws Exception {
+		var page = html("/page");
+		assertEquals(4, AssetResolutionAssert.assetUrls(page).size());
+		AssetResolutionAssert.assertAssetsResolve("/page", page, AssetResolutionAssert_Test::status);
+	}
+
+	@Test void a02_theChromeLinksItsMountedAssets() throws Exception {
+		var urls = String.join("\n", AssetResolutionAssert.assetUrls(html("/page")));
+		assertContainsAll(urls, "/juneau-console/chrome.css", "/juneau-console/juneau-console.js", "/juneau-console/themes/juneau-theme-gray.css", "/logo.svg");
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// b - the helper itself
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_missingAssetIsReported() {
+		var html = "<link rel=\"stylesheet\" href=\"/juneau-console/chrome.css\"><script src=\"/juneau-console/gone.js\"></script>";
+		assertList(AssetResolutionAssert.unresolved(html, AssetResolutionAssert_Test::status), "404 /juneau-console/gone.js");
+		var e = assertThrows(AssertionError.class, () -> AssetResolutionAssert.assertAssetsResolve("/p", html, AssetResolutionAssert_Test::status));
+		assertContainsAll(e.getMessage(), "Unresolved assets linked from /p", "404 /juneau-console/gone.js");
+	}
+
+	@Test void b02_onlyAssetLinksAndSameOriginUrlsAreChecked() {
+		var html = "<link rel=\"canonical\" href=\"/c\">"
+			+ "<link rel=\"stylesheet\" href=\"https://cdn.example.com/x.css\">"
+			+ "<script src=\"//cdn.example.com/x.js\"></script>"
+			+ "<img src=\"data:image/png;base64,AAAA\">"
+			+ "<script data-src=\"/not-an-asset.js\"></script>"
+			+ "<script>var s = 1;</script>"
+			+ "<link rel=\"icon\" href=\"/i.ico#frag\">"
+			+ "<script src=\"/a.js?x=1&amp;y=2\"></script>"
+			+ "<script src=\"/a.js?x=1&amp;y=2\"></script>";
+		assertList(AssetResolutionAssert.assetUrls(html), "/i.ico", "/a.js?x=1&y=2");
+	}
+
+	@Test void b03_noAssetsPasses() {
+		AssetResolutionAssert.assertAssetsResolve("<p>none</p>", u -> 500);
+		assertEquals(List.of(), AssetResolutionAssert.unresolved("<p>none</p>", u -> 500));
+	}
+}
diff --git a/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java b/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
index 8c4d9ff12a..c2b19323ed 100644
--- a/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
+++ b/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
@@ -74,7 +74,8 @@ import jakarta.servlet.http.*;
  * 	<li><b>SSE</b> ({@code text/event-stream}) &mdash; each element is emitted as a Server-Sent-Events
  * 		frame. {@link SseEvent} elements are written verbatim; any other element type is JSON-encoded
  * 		into the {@code data:} field.
- * 	<li><b>NDJSON</b> ({@code application/x-ndjson}, {@code application/jsonl}, {@code application/json5l}) &mdash; each element is
+ * 	<li><b>NDJSON</b> ({@code application/x-ndjson}, {@code application/jsonl}, {@code application/jsonlines},
+ * 		{@code application/json5l}, {@code application/json5lines}) &mdash; each element is
  * 		JSON-encoded on its own line.
  * 	<li><b>Buffer</b> (default, any other media type) &mdash; all elements are collected into a
  * 		{@link List List} and serialized through the normal serializer chain (e.g. a JSON
diff --git a/juneau-rest/juneau-rest-server-reactive/src/test/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor_Test.java b/juneau-rest/juneau-rest-server-reactive/src/test/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor_Test.java
index 3ad46739da..b1223592e6 100644
--- a/juneau-rest/juneau-rest-server-reactive/src/test/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor_Test.java
+++ b/juneau-rest/juneau-rest-server-reactive/src/test/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor_Test.java
@@ -362,6 +362,20 @@ class ReactiveResponseProcessor_Test extends TestBase {
 		assertTrue(c.contains("\"name\":\"p\""), c);
 	}
 
+	@Test void f02_acceptJsonlines_triggersNdjsonShape() throws Exception {
+		var c = CF.get("/ndjsonViaJsonl").header("Accept", "application/jsonlines").run()
+			.assertStatus(200).getContent().asString();
+		assertTrue(c.contains("\"name\":\"p\""), c);
+		assertFalse(c.startsWith("["), c);
+	}
+
+	@Test void f03_acceptJson5lines_triggersNdjsonShape() throws Exception {
+		var c = CF.get("/ndjsonViaJsonl").header("Accept", "application/json5lines").run()
+			.assertStatus(200).getContent().asString();
+		assertTrue(c.contains("\"name\":\"p\""), c);
+		assertFalse(c.startsWith("["), c);
+	}
+
 	// -----------------------------------------------------------------------------------------------------------------
 	// G: CharSequence elements — writeSseFrame (line 288) and writeNdjsonFrame (line 298) each have a
 	// CharSequence branch that writes the element's toString() directly instead of JSON-encoding it.
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-config.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-config.js
index bf3dc3bddc..1839c3a7bd 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-config.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-config.js
@@ -299,7 +299,7 @@
 
 	/** Rejects an unknown/newer blob schemaVersion as 'malformed' (§3.2 stale-load handling) - never crashes. */
 	function assertSupportedSchema(blob) {
-		if (!blob || blob.schemaVersion !== CURRENT_SCHEMA_VERSION)
+		if (blob?.schemaVersion !== CURRENT_SCHEMA_VERSION)
 			throw malformedError("saved-view blob has an unsupported schemaVersion (expected " + CURRENT_SCHEMA_VERSION + ")");
 		return blob;
 	}
@@ -444,7 +444,7 @@
 	function defaultVisibleKeys(catalog) {
 		const out = [];
 		(catalog || []).forEach(function (c) {
-			if (!c || c.data == null) return;
+			if (c?.data == null) return;
 			if (c.pinned || c.defaultVisible !== false) out.push(c.data);
 		});
 		return out;
@@ -623,7 +623,7 @@
 		let blob = raw;
 		if (typeof raw === "string") {
 			try { blob = JSON.parse(raw); }
-			catch (e) { throw malformedError("saved-view blob is not valid JSON"); }
+			catch (e) { throw malformedError("saved-view blob is not valid JSON"); } // NOSONAR javascript:S2486 -- the parse failure is rethrown as a typed malformed error
 		}
 		assertSupportedSchema(blob);
 		return {
@@ -644,7 +644,7 @@
 	/** Non-blank label overrides only (a blank override means "use catalog title" and is omitted from the blob). */
 	function serializeLabels(view) {
 		const out = {};
-		if (!view || !view.labels || typeof view.labels !== "object") return out;
+		if (!view?.labels || typeof view.labels !== "object") return out;
 		for (const k in view.labels) {
 			if (!Object.hasOwn(view.labels, k)) continue;
 			const v = view.labels[k];
@@ -656,7 +656,7 @@
 	/** Format overrides only (nullish values are omitted from the blob). */
 	function serializeFormats(view) {
 		const out = {};
-		if (!view || !view.formats || typeof view.formats !== "object") return out;
+		if (!view?.formats || typeof view.formats !== "object") return out;
 		for (const k in view.formats)
 			if (Object.hasOwn(view.formats, k) && view.formats[k] != null) out[k] = view.formats[k];
 		return out;
@@ -737,7 +737,7 @@
 	function readActiveRaw(scope) {
 		const raw = window.localStorage.getItem(activeKeyFor(scope));
 		if (raw == null) return null;
-		try { return decSegment(raw); } catch (e) { return raw; }
+		try { return decSegment(raw); } catch (e) { return raw; } // NOSONAR javascript:S2486 -- an undecodable segment falls back to the raw text
 	}
 
 	// NOSONAR javascript:S7721 -- must stay inside this file's module IIFE: hoisting past the closing `})()`
@@ -757,7 +757,7 @@
 		for (let i = 0; i < window.localStorage.length; i++) {
 			const k = window.localStorage.key(i);
 			if (k?.startsWith(prefix)) {
-				try { out.push({ name: decSegment(k.slice(prefix.length)) }); } catch (e) { /* skip unreadable key */ }
+				try { out.push({ name: decSegment(k.slice(prefix.length)) }); } catch (e) { /* skip unreadable key */ } // NOSONAR javascript:S2486 -- an unreadable key is skipped
 			}
 		}
 		return out;
@@ -790,24 +790,24 @@
 			// every consumer (in this file and the chooser UI) reads only `.code`/`.message`, and widening
 			// this to an Error would risk changing enumerable-property/JSON-serialization behavior for
 			// callers outside this file that we cannot fully audit.
-			try { resolve(fn()); } catch (e) { reject(toTypedError(e)); }
+			try { resolve(fn()); } catch (e) { reject(toTypedError(e)); } // NOSONAR javascript:S6671 -- typed {code,message} failure contract, not an Error subclass; see above
 		});
 	}
 
-	function createLocalStorageProvider() {
+	/** Enforces the per-blob/per-scope/per-user bounds (§3.2) INSIDE the write op, never as a racy pre-flight. */
+	function enforceBounds(scope, name, blob) {
+		const json = JSON.stringify(blob);
+		if (byteLength(json) > LOCALSTORAGE_MAX_BLOB_BYTES)
+			throw quotaError("saved view exceeds the per-blob size cap (" + LOCALSTORAGE_MAX_BLOB_BYTES + " bytes)");
+		const existing = listViews(scope);
+		const isReplace = existing.some(function (v) { return v.name === name; });
+		if (!isReplace && existing.length >= LOCALSTORAGE_MAX_VIEWS_PER_SCOPE)
+			throw quotaError("scope already has " + LOCALSTORAGE_MAX_VIEWS_PER_SCOPE + " saved views (MAX_VIEWS_PER_SCOPE)");
+		if (!isReplace && countAllViewsForThisUser() >= LOCALSTORAGE_MAX_VIEWS_PER_USER)
+			throw quotaError("aggregate saved-view count reached MAX_VIEWS_PER_USER (" + LOCALSTORAGE_MAX_VIEWS_PER_USER + ")");
+	}
 
-		/** Enforces the per-blob/per-scope/per-user bounds (§3.2) INSIDE the write op, never as a racy pre-flight. */
-		function enforceBounds(scope, name, blob) {
-			const json = JSON.stringify(blob);
-			if (byteLength(json) > LOCALSTORAGE_MAX_BLOB_BYTES)
-				throw quotaError("saved view exceeds the per-blob size cap (" + LOCALSTORAGE_MAX_BLOB_BYTES + " bytes)");
-			const existing = listViews(scope);
-			const isReplace = existing.some(function (v) { return v.name === name; });
-			if (!isReplace && existing.length >= LOCALSTORAGE_MAX_VIEWS_PER_SCOPE)
-				throw quotaError("scope already has " + LOCALSTORAGE_MAX_VIEWS_PER_SCOPE + " saved views (MAX_VIEWS_PER_SCOPE)");
-			if (!isReplace && countAllViewsForThisUser() >= LOCALSTORAGE_MAX_VIEWS_PER_USER)
-				throw quotaError("aggregate saved-view count reached MAX_VIEWS_PER_USER (" + LOCALSTORAGE_MAX_VIEWS_PER_USER + ")");
-		}
+	function createLocalStorageProvider() {
 
 		function persistBlob(scope, name, blob) {
 			const v = validateNameForLocalStorage(name);
@@ -834,7 +834,7 @@
 					const raw = window.localStorage.getItem(viewKeyFor(ctx.scope, v.encoded));
 					if (raw == null) return null;
 					let blob;
-					try { blob = JSON.parse(raw); } catch (e) { throw malformedError("stored saved-view blob is not valid JSON"); }
+					try { blob = JSON.parse(raw); } catch (e) { throw malformedError("stored saved-view blob is not valid JSON"); } // NOSONAR javascript:S2486 -- the parse failure is rethrown as a typed malformed error
 					return assertSupportedSchema(blob);
 				});
 			},
@@ -960,7 +960,7 @@
 	function readJsonBody(resp) {
 		return resp.text().then(function (text) {
 			if (text == null || text === "") return null;
-			try { return JSON.parse(text); } catch (e) { throw malformedError("saved-views response was not valid JSON"); }
+			try { return JSON.parse(text); } catch (e) { throw malformedError("saved-views response was not valid JSON"); } // NOSONAR javascript:S2486 -- the parse failure is rethrown as a typed malformed error
 		});
 	}
 
@@ -999,31 +999,31 @@
 			// changing enumerable-property/JSON-serialization behavior for callers we cannot fully audit.
 			return Promise.resolve(fn()).then(null, function (e) { throw toTypedError(e); });
 		} catch (e) {
-			return Promise.reject(toTypedError(e));
+			return Promise.reject(toTypedError(e)); // NOSONAR javascript:S6671 -- typed {code,message} failure contract, not an Error subclass; see above
 		}
 	}
 
-	function createServerProvider() {
+	function queryFor(table, extra) {
+		const params = { view: requireViewId(table) };
+		const pageId = resolvePageId(table);
+		if (pageId != null) params.page = pageId;
+		if (extra) for (const k in extra) if (Object.hasOwn(extra, k) && extra[k] != null) params[k] = extra[k];
+		return buildQuery(params);
+	}
 
-		function queryFor(table, extra) {
-			const params = { view: requireViewId(table) };
-			const pageId = resolvePageId(table);
-			if (pageId != null) params.page = pageId;
-			if (extra) for (const k in extra) if (Object.hasOwn(extra, k) && extra[k] != null) params[k] = extra[k];
-			return buildQuery(params);
-		}
+	function httpError(status, bodyText) {
+		let env = null;
+		try { env = bodyText ? JSON.parse(bodyText) : null; } catch (e) { env = null; /* non-JSON body: fall back to the generic HTTP-status message below */ } // NOSONAR javascript:S2486 -- a non-JSON body falls back to the generic HTTP-status message
+		const err = typedError(classifyStatus(status), env?.message ? env.message : ("saved-views request failed (HTTP " + status + ")"));
+		err.httpStatus = status;
+		return err;
+	}
 
-		function httpError(status, bodyText) {
-			let env = null;
-			try { env = bodyText ? JSON.parse(bodyText) : null; } catch (e) { env = null; /* non-JSON body: fall back to the generic HTTP-status message below */ }
-			const err = typedError(classifyStatus(status), env?.message ? env.message : ("saved-views request failed (HTTP " + status + ")"));
-			err.httpStatus = status;
-			return err;
-		}
+	function createServerProvider() {
 
 		function doFetch(url, init) {
 			let req;
-			try { req = fetch(url, init); } catch (e) { return Promise.reject(networkError("the saved-views request could not be sent")); }
+			try { req = fetch(url, init); } catch (e) { return Promise.reject(networkError("the saved-views request could not be sent")); } // NOSONAR javascript:S2486 javascript:S4822 -- a synchronous fetch throw becomes a typed network error; fetch returns a promise otherwise
 			return req.then(function (resp) {
 				if (resp.ok) return resp;
 				return resp.text().then(function (text) { throw httpError(resp.status, text); },
@@ -1172,12 +1172,12 @@
 	NS.persistence.getItem = function (key) {
 		try {
 			return window.localStorage.getItem(key);
-		} catch (e) { return null; /* quota / private mode — ribbon click path stays synchronous and must not throw */ }
+		} catch (e) { return null; /* quota / private mode — ribbon click path stays synchronous and must not throw */ } // NOSONAR javascript:S2486 -- storage quota or private mode must not throw on the synchronous ribbon click path
 	};
 	NS.persistence.setItem = function (key, value) {
 		try {
 			window.localStorage.setItem(key, String(value));
-		} catch (e) { /* quota / private mode — ribbon click path stays synchronous and must not throw */ }
+		} catch (e) { /* quota / private mode — ribbon click path stays synchronous and must not throw */ } // NOSONAR javascript:S2486 -- storage quota or private mode must not throw on the synchronous ribbon click path
 	};
 
 	// Exposed for the pure-logic/source-shape tests and for later slices (chooser UI, config-application layer).
@@ -1225,11 +1225,11 @@
 	function resolveActiveView(table, viewDef) {
 		if (!NS.persistence) return Promise.resolve(null);
 		return NS.persistence.getActive(table).then(function (r) {
-			if (!r || r.name == null) return null;
+			if (r?.name == null) return null;
 			return NS.persistence.load(table, r.name).then(function (blob) {
 				if (blob == null) return null;
 				try { return deserializeSavedView(blob); }
-				catch (e) { return null; /* stale/unknown-schema blob resolves as Default, per the doc above */ }
+				catch (e) { return null; /* stale/unknown-schema blob resolves as Default, per the doc above */ } // NOSONAR javascript:S2486 -- a stale or unknown-schema blob resolves as Default
 			});
 		});
 	}
@@ -1244,7 +1244,7 @@
 	 */
 	function applyView(table, savedView, overrides) {
 		const ctx = table?.__juneauCtx;
-		if (!ctx || !ctx.viewDef) return { ok: false, reason: "not-initialized" };
+		if (!ctx?.viewDef) return { ok: false, reason: "not-initialized" };
 		if (!NS.init || typeof NS.init.buildTable !== "function") return { ok: false, reason: "no-buildTable" };
 		let effective;
 		try {
@@ -1255,7 +1255,7 @@
 		if (overrides?.searchMembership != null)
 			effective = applySearchMembershipToColumns(effective, overrides.searchMembership);
 		const viewDef = overrides?.defaultOrder
-			? Object.assign({}, ctx.viewDef, { defaultOrder: overrides.defaultOrder })
+			? { ...ctx.viewDef, defaultOrder: overrides.defaultOrder }
 			: ctx.viewDef;
 		return NS.init.buildTable(table, viewDef, effective, ctx);
 	}
@@ -1379,7 +1379,7 @@
 		if (searchMembership == null) return effectiveColumns;
 		const member = new Set(searchMembership);
 		return (effectiveColumns || []).map(function (c) {
-			if (!c || c.data == null || c.searchable === false || member.has(c.data)) return c;
+			if (c?.data == null || c.searchable === false || member.has(c.data)) return c;
 			const copy = {};
 			for (const k in c) if (Object.hasOwn(c, k)) copy[k] = c[k];
 			copy.searchable = false;
@@ -1402,7 +1402,7 @@
 		const out = [];
 		const seen = Object.create(null);
 		sortList.forEach(function (e) {
-			if (!e || e.column == null) return;
+			if (e?.column == null) return;
 			const key = String(e.column);
 			if (!allowed[key] || seen[key]) return;
 			seen[key] = true;
@@ -1418,7 +1418,7 @@
 		// (intersected with the sort-capable columns) so an untouched Apply never replaces it with "every column".
 		if (Array.isArray(defaultOrder)) {
 			return intersectSortOrder(defaultOrder.map(function (e) {
-				return { column: e && e.data, dir: e && e.dir };
+				return { column: e?.data, dir: e?.dir };
 			}), capable);
 		}
 		return intersectSortOrder(null, capable);
@@ -1467,7 +1467,7 @@
 		const order = [];
 		const visible = [];
 		cols.forEach(function (c) {
-			if (!c || c.data == null) return;
+			if (c?.data == null) return;
 			order.push(c.data);
 			if (c.pinned || c.defaultVisible !== false) visible.push(c.data);
 		});
@@ -1634,7 +1634,7 @@
 	 */
 	function copyShareLink(table) {
 		if (typeof NS.init?.copyShareableUrl === "function")
-			return NS.init.copyShareableUrl(table, table && table.__juneauCtx);
+			return NS.init.copyShareableUrl(table, table?.__juneauCtx);
 		if (!NS.urlState || typeof NS.urlState.buildShareUrl !== "function")
 			return Promise.resolve({ ok: false, url: "" });
 		const url = NS.urlState.buildShareUrl(window.location, { tab: null, filters: [], sort: null });
@@ -1652,7 +1652,7 @@
 		const byData = catalogByDataLocal(catalog);
 		const col = byData[dataKey];
 		if (col?.pinned) return false;
-		if (!draft || !draft.visible) return false;
+		if (!draft?.visible) return false;
 		if (draft.visible.indexOf(dataKey) < 0) return true;
 		return visibleCount(draft) > 1;
 	}
@@ -2605,10 +2605,10 @@
 	function stampChromeTip(el, text) {
 		const t = text == null ? "" : String(text);
 		if (t !== "") {
-			el.setAttribute("data-jc-tip", t);
+			el.setAttribute("data-jc-tip", t); // NOSONAR javascript:S7761 -- el may be a minimal stub without dataset; the attribute name is pinned by CSS selectors
 			el.setAttribute("aria-label", t);
 		} else if (typeof el.removeAttribute === "function") {
-			el.removeAttribute("data-jc-tip");
+			el.removeAttribute("data-jc-tip"); // NOSONAR javascript:S7761 -- el may be a minimal stub without dataset; the attribute name is pinned by CSS selectors
 		}
 		if (typeof el.removeAttribute === "function") el.removeAttribute("title");
 		el.title = "";
@@ -2619,7 +2619,7 @@
 	 * {@code constructTable} on first init AND every Apply rebuild.
 	 */
 	function mountChooser(table, ctx, toolbarRow) {
-		if (!ctx || !ctx.viewDef || !ctx.viewDef.columnConfig) return;
+		if (!ctx?.viewDef?.columnConfig) return;
 		const host = resolveChooserHost(table, toolbarRow);
 		if (!host) return;
 		if (host.querySelector?.(".juneau-config-chooser-btn")) return;
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-helpers.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-helpers.js
index 890a2fc458..fafbb145c2 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-helpers.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-helpers.js
@@ -105,7 +105,7 @@
 		if (template == null) return null;
 		const map = isPlainObject(values) ? values : {};
 		// NOSONAR javascript:S8786 -- `[^}]+` cannot match `}`; same analysis as interpolateHref in juneau-renders.js; the template is developer-authored, never row data.
-		const url = String(template).replace(/\{([^}]+)\}/g, function (m, key) {
+		const url = String(template).replace(/\{([^}]+)\}/g, function (m, key) { // NOSONAR javascript:S5852 -- `[^}]+` cannot match `}`, so matching is linear; template is developer-authored
 			const v = Object.hasOwn(map, key) ? map[key] : undefined;
 			return v == null ? "" : encodeURIComponent(String(v));
 		});
@@ -312,7 +312,7 @@
 	// ================================================================================================
 
 	// NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
-	function editableField(opts) {
+	function editableField(opts) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent
 		opts = opts || {};
 		if (typeof opts.onSave !== "function")
 			throw new TypeError("JuneauViews.helpers: editableField(opts) requires opts.onSave to be a function.");
@@ -358,7 +358,7 @@
 			return committed == null ? "" : String(committed);
 		}
 
-		function readControl() {
+		function readControl() { // NOSONAR javascript:S3800 -- returns the control value (boolean for checkbox, string otherwise); callers normalise
 			if (!control) return committed;
 			if (type === "checkbox") return !!control.checked;
 			return control.value == null ? "" : String(control.value);
@@ -416,7 +416,7 @@
 			if (mode === "saving") mode = type === "checkbox" ? "view" : "edit";
 		}
 
-		function applySaved(submitted, result) {
+		function applySaved(submitted, result) { // NOSONAR javascript:S3800 -- returns the control value (boolean for checkbox, string otherwise); callers normalise
 			if (result == null) return type === "checkbox" ? !!submitted : String(submitted);
 			if (type === "checkbox") return !!result;
 			return String(result);
@@ -446,7 +446,7 @@
 					renderView();
 					if (pencil?.focus) pencil.focus();
 				}, function (err) {
-					if (gen !== saveGen) return Promise.reject(err);
+					if (gen !== saveGen) throw err;
 					inFlight = null;
 					const msg = saveErrorMessage(err);
 					toast(msg, { tone: "error" });
@@ -456,13 +456,13 @@
 						mode = "view";
 						showError(msg);
 						if (control.focus) control.focus();
-						return Promise.reject(err);
+						throw err;
 					}
 					setSaving(false);
 					mode = "edit";
 					showError(msg);
 					if (control?.focus) control.focus();
-					return Promise.reject(err);
+					throw err;
 				});
 			return inFlight;
 		}
@@ -506,7 +506,7 @@
 			if (errorText) clearError();
 		}
 
-		function buildTextOrSelect(value) {
+		function buildTextOrSelect(value) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent
 			let el;
 			if (type === "select") {
 				el = document.createElement("select");
@@ -742,7 +742,7 @@
 	}
 
 	// NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
-	function fieldGrid(fields, opts) {
+	function fieldGrid(fields, opts) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent
 		if (!Array.isArray(fields))
 			throw new TypeError("JuneauViews.helpers: fieldGrid(fields, opts) requires fields to be an array of field descriptions.");
 		opts = opts || {};
@@ -881,7 +881,7 @@
 	// picks F34 up once it is confirmed.  Flagged in this child's build report.
 	// ================================================================================================
 
-	function recordTable(catalog, rows, opts) {
+	function recordTable(catalog, rows, opts) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent
 		if (!Array.isArray(catalog))
 			throw new TypeError("JuneauViews.helpers: recordTable(catalog, rows, opts) requires catalog to be an array.");
 		if (!Array.isArray(rows))
@@ -915,8 +915,7 @@
 		for (const rowsEntry of rows) {
 			const rowValues = isPlainObject(rowsEntry) ? rowsEntry : {};
 			const tr = document.createElement("tr");
-			for (let ci = 0; ci < columns.length; ci++) {
-				const field = columns[ci];
+			for (const field of columns) {
 				const td = document.createElement("td");
 				paintFieldValue(td, field, rowValues, null);
 				if (!field.render && field.href) {
@@ -984,7 +983,7 @@
 		const seen = {};
 		for (let i = 0; i < tabs.length; i++) {
 			const t = tabs[i];
-			if (!t || t.id == null || t.label == null)
+			if (t?.id == null || t.label == null)
 				throw new TypeError("JuneauViews.helpers: tabStrip entry " + i + " requires an id and a label.");
 			const id = String(t.id);
 			if (Object.hasOwn(seen, id))
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-icons.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-icons.js
index 2b1ad10ab1..5abcfd6b0b 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-icons.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-icons.js
@@ -295,7 +295,7 @@
 	function parseSvg(xml) {
 		const doc = new DOMParser().parseFromString(xml, "image/svg+xml");
 		const root = doc.documentElement;
-		if (!root || root.nodeName.toLowerCase() !== "svg" || root.querySelector("parsererror"))
+		if (root?.nodeName.toLowerCase() !== "svg" || root.querySelector("parsererror"))
 			return null;
 		return root;
 	}
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-pagestate.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-pagestate.js
index ef93f0d3d5..6433f0addf 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-pagestate.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-pagestate.js
@@ -57,7 +57,7 @@
 			ls.setItem(probe, '1');   // a blocked-storage browser throws here, not on the property read above.
 			ls.removeItem(probe);
 			return ls;
-		} catch (e) {
+		} catch (e) { // NOSONAR javascript:S2486 -- storage blocked: degrade to the no-op store
 			// Storage is blocked: degrade to the no-op store (the error itself carries nothing actionable).
 			return NOOP_STORE;
 		}
@@ -67,22 +67,22 @@
 
 	// Every store call is guarded: a store that starts fine but later throws (quota, eviction) degrades to a no-op
 	// for THAT call rather than surfacing to the caller.
-	function rawGet(key) { try { return store.getItem(key); } catch (e) { return null; } }
-	function rawSet(key, value) { try { store.setItem(key, value); } catch (e) { /* no-op */ } }
-	function rawRemove(key) { try { store.removeItem(key); } catch (e) { /* no-op */ } }
+	function rawGet(key) { try { return store.getItem(key); } catch (e) { return null; } } // NOSONAR javascript:S2486 -- a store that throws later degrades to null for that call
+	function rawSet(key, value) { try { store.setItem(key, value); } catch (e) { /* no-op */ } } // NOSONAR javascript:S2486 -- a store that throws later degrades to a no-op for that call
+	function rawRemove(key) { try { store.removeItem(key); } catch (e) { /* no-op */ } } // NOSONAR javascript:S2486 -- a store that throws later degrades to a no-op for that call
 
 	/** Reads and JSON-decodes `key`; a missing key OR an undecodable value both read as null. */
 	function get(key) {
 		const raw = rawGet(key);
 		if (raw == null) return null;
-		try { return JSON.parse(raw); } catch (e) { return null; }
+		try { return JSON.parse(raw); } catch (e) { return null; } // NOSONAR javascript:S2486 -- an undecodable value reads as null
 	}
 
 	/** JSON-encodes and writes `value` under `key`; an `undefined` value (or an unserializable one) clears it. */
 	function set(key, value) {
 		if (value === undefined) { rawRemove(key); return; }
 		let enc;
-		try { enc = JSON.stringify(value); } catch (e) { return; }
+		try { enc = JSON.stringify(value); } catch (e) { return; } // NOSONAR javascript:S2486 -- an unserializable value is not stored
 		rawSet(key, enc);
 	}
 
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-regions.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-regions.js
index ef427881ee..1887717f79 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-regions.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-regions.js
@@ -976,7 +976,7 @@
 	/** A never-throwing JSON parse - a malformed body is a fail-closed rejection, not an uncaught exception. */
 	function safeParseJson(text) {
 		if (blank(text)) return null;
-		try { return JSON.parse(text); } catch (e) { return null; }
+		try { return JSON.parse(text); } catch (e) { return null; } // NOSONAR javascript:S2486 -- a non-JSON body is treated as no data
 	}
 
 	function declaredFetchError(kind, message) {
@@ -1169,7 +1169,7 @@
 		}).then(function (res) {
 			return res.text().then(function (text) {
 				let data = null;
-				try { data = text ? JSON.parse(text) : null; } catch (e) { data = null; }
+				try { data = text ? JSON.parse(text) : null; } catch (e) { data = null; } // NOSONAR javascript:S2486 -- a non-JSON body is treated as no data
 				return { ok: res.ok, status: res.status, data: data };
 			});
 		});
@@ -1247,7 +1247,7 @@
 
 	/** The client twin of {@code RegionDef.MIN_REFRESH_MS}'s clamp - belt-and-suspenders over the server's own. */
 	function clampRefreshMs(ms) {
-		return ms < MIN_REFRESH_MS ? MIN_REFRESH_MS : ms;
+		return Math.max(ms, MIN_REFRESH_MS);
 	}
 
 	/**
@@ -1721,7 +1721,7 @@
 	 * @param {Object<string, string|{table: string|Object}>} hookup Map of element id → populator name or table binding.
 	 * @returns {Promise<Array>} Thenable resolving to the region handles initRegion minted.
 	 */
-	function mount(hookup) {
+	function mount(hookup) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent
 		if (hookup == null || typeof hookup !== "object" || Array.isArray(hookup)) {
 			const message = "JuneauViews.regions.mount: expected a map of slot id -> populator name.";
 			window.console.error(message);
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-renders.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-renders.js
index fa691a2964..b6c0a872f8 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-renders.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-renders.js
@@ -94,7 +94,7 @@
 	// as-is: `[^}]` → `[^{}]` would change the matched language, and this is fed developer-authored `meta.href`,
 	// never row data.
 	function interpolateHref(template, rowData) {
-		return String(template).replace(/\{([^}]+)\}/g, function (m, key) {
+		return String(template).replace(/\{([^}]+)\}/g, function (m, key) { // NOSONAR javascript:S5852 -- `[^}]+` cannot match `}`, so matching is linear; template is developer-authored
 			const v = rowData ? rowData[key] : undefined;
 			return v == null ? "" : encodeURIComponent(String(v));
 		});
@@ -122,8 +122,8 @@
 		// for ~2.2s (javascript:S5852).  This walk is linear and yields exactly the same string.
 		let i = 0;
 		let j = collapsed.length;
-		while (i < j && collapsed.charCodeAt(i) === 45) i++;
-		while (j > i && collapsed.charCodeAt(j - 1) === 45) j--;
+		while (i < j && collapsed.codePointAt(i) === 45) i++;
+		while (j > i && collapsed.codePointAt(j - 1) === 45) j--;
 		return collapsed.slice(i, j);
 	}
 
@@ -272,7 +272,7 @@
 			if (cellData == null) return "";
 			let s;
 			// A circular/BigInt/etc. value that JSON.stringify refuses: fall back to the plain string form.
-			try { s = JSON.stringify(cellData); } catch (e) { s = String(cellData); }
+			try { s = JSON.stringify(cellData); } catch (e) { s = String(cellData); } // NOSONAR javascript:S2486 -- an unserializable cell falls back to String()
 			return "<code>" + escHtml(s) + "</code>";
 		}
 	});
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-ribbon.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-ribbon.js
index 0b28e28a10..1529e56040 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-ribbon.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-ribbon.js
@@ -61,7 +61,7 @@
 	 * {@code dtIndex} (selection offset + client reorder).
 	 */
 	function optionParam(viewDef, opt, optsColumns) {
-		if (opt == null || opt.value == null) return null;
+		if (opt?.value == null) return null;
 		if (opt.column != null) {
 			const idx = indexForRibbonColumn(viewDef, opt.column, optsColumns);
 			if (idx < 0) return null;
@@ -367,7 +367,7 @@
 		if (!moving.length) return actions;
 		const kept = actions.filter(function (a) { return !(a.type === "refresh" && a.group == null); });
 		while (kept.length && kept.at(-1).type === "divider") kept.pop();
-		const moved = moving.map(function (a) { return Object.assign({}, a, { group: "__refresh" }); });
+		const moved = moving.map(function (a) { return { ...a, group: "__refresh" }; });
 		return kept.concat(moved);
 	}
 
@@ -377,7 +377,7 @@
 
 	function safeStorage() {
 		// A SecurityError (private-mode/disabled storage) means no storage is available - not an error to surface.
-		try { return window.localStorage; } catch (e) { return null; }
+		try { return window.localStorage; } catch (e) { return null; } // NOSONAR javascript:S2486 -- storage access can throw in privacy modes; treat as unavailable
 	}
 
 	/**
@@ -467,7 +467,7 @@
 			openGroup.el.appendChild(el);
 		}
 
-		actions.forEach(function (a) {
+		actions.forEach(function (a) { // NOSONAR javascript:S3776 -- one dispatch branch per RibbonAction.type; complexity is inherent
 			if (a.type === "divider") {
 				openGroup = null;
 				const d = document.createElement("span");
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-search.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-search.js
index 988625c758..4729a311a9 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-search.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-search.js
@@ -117,8 +117,8 @@
 	];
 
 	const BUILTINS = {};
-	for (let bi = 0; bi < BUILTIN_LIST.length; bi++)
-		BUILTINS[BUILTIN_LIST[bi].name.toLowerCase()] = BUILTIN_LIST[bi];
+	for (const builtin of BUILTIN_LIST)
+		BUILTINS[builtin.name.toLowerCase()] = builtin;
 
 	const SearchOperators = {
 		/** All built-in operators in canonical order. */
@@ -239,7 +239,7 @@
 		} else {
 			body = s;
 		}
-		return body.split('$$').join('$');
+		return body.replaceAll('$$', '$');
 	}
 
 	/** Parses one (already trimmed, non-empty) token into a node. */
@@ -269,7 +269,7 @@
 	 * comma-separated argument slot was empty (a stray or trailing comma) - `$blank()`'s zero-argument call is
 	 * NOT a blank arg.
 	 */
-	function scanArgs(s, open) {
+	function scanArgs(s, open) { // NOSONAR javascript:S3776 -- recursive-descent parsing; complexity is inherent
 		let state = S1, depth = 0, argStart = open + 1, blankArg = false;
 		const rawArgs = [];
 
@@ -505,7 +505,7 @@
 	// Signed ISO-8601 duration (mirror of ValueParse.isoDurationMillis / java.time.Duration.parse): one leading sign only,
 	// days/hours/minutes/seconds (fractional seconds, '.' or ','), case-insensitive; years/months/weeks and per-component signs are invalid.
 	const ISO_START_RE = /^[+-]?P/i;
-	const ISO_RE = /^([+-])?P(?:(\d+)D)?(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:[.,](\d{0,9}))?S)?)?$/i;
+	const ISO_RE = /^([+-])?P(?:(\d+)D)?(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:[.,](\d{0,9}))?S)?)?$/i; // NOSONAR javascript:S5843 -- ISO-8601 duration grammar; splitting the pattern would obscure it
 	const UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
 	const LONG_MAX = typeof BigInt === "function" ? BigInt("9223372036854775807") : null;
 
@@ -533,7 +533,7 @@
 	}
 
 	/** Signed milliseconds (fractions floored) of an ISO-8601 duration; NaN when it is malformed or its total overflows a Java long (Java: BAD_VALUE). */
-	function isoDurationMillis(v) {
+	function isoDurationMillis(v) { // NOSONAR javascript:S3776 -- one branch per ISO-8601 duration component; complexity is inherent
 		const m = ISO_RE.exec(v);
 		if (m == null || (m[3] === "T" && m[4] == null && m[5] == null && m[6] == null) || (m[2] == null && m[3] == null))
 			return Number.NaN;
@@ -634,7 +634,7 @@
 	function eqCi(cell, arg, type) {
 		if (type === T_TEXT || type === T_ID || type === T_ENUM) {
 			const v = strVal(cell);
-			return v != null && v.toLowerCase() === String(arg).toLowerCase();
+			return v?.toLowerCase() === String(arg).toLowerCase();
 		}
 		return typedEquals(cell, arg, type);
 	}
@@ -704,14 +704,14 @@
 
 	function contains(cell, arg) {
 		const v = strVal(cell);
-		return v != null && v.toLowerCase().includes(String(arg).toLowerCase());
+		return v?.toLowerCase().includes(String(arg).toLowerCase()) === true;
 	}
 
 	function prefix(cell, arg, type) {
 		if (type === T_VER)
 			return versionPrefix(strVal(cell), arg);
 		const v = strVal(cell);
-		return v != null && v.toLowerCase().startsWith(String(arg).toLowerCase());
+		return v?.toLowerCase().startsWith(String(arg).toLowerCase()) === true;
 	}
 
 	/** Translates the portable i/m/s flag chars into a JS RegExp flag string (design section 4.1). */
@@ -735,7 +735,7 @@
 		try {
 			// Java Matcher.matches() is a full-string match; anchor the pattern to reproduce it.
 			return new RegExp("^(?:" + args[0] + ")$", flags);
-		} catch (e) {
+		} catch (e) { // NOSONAR javascript:S2486 -- an invalid regex simply does not match
 			return null;  // Bad pattern matches nothing (PatternSyntaxException parity).
 		}
 	}
@@ -745,13 +745,13 @@
 		if (v == null)
 			return false;
 		const re = buildRegex(args);
-		return re != null && re.test(v);
+		return re?.test(v) === true;
 	}
 
 	/** `$regex` against a pattern `compile()` already built once (null = bad pattern, which matches nothing). */
 	function matchCompiledRegex(re, cell) {
 		const v = strVal(cell);
-		return v != null && re != null && re.test(v);
+		return v != null && re?.test(v) === true;
 	}
 
 	function between(cell, args, type) {
@@ -1021,7 +1021,7 @@
 	 * Mirrors Java's `SearchExpressionParser` throw sites one for one (MALFORMED_OPERATOR, INVALID_OPERATOR_NAME,
 	 * UNKNOWN_OPERATOR, UNTERMINATED_QUOTE, UNBALANCED, EMPTY_ARGUMENT, TRAILING_TEXT, BAD_ARG_COUNT).
 	 */
-	function parseFuncStrict(s, customs) {
+	function parseFuncStrict(s, customs) { // NOSONAR javascript:S3776 -- recursive-descent parsing; complexity is inherent
 		if (s.length > 1 && s.charAt(0) === '$' && IS_LETTER.test(s.charAt(1))) {
 			const open = s.indexOf('(');
 			if (open < 0)
@@ -1187,7 +1187,7 @@
 			return;
 		}
 		const o = SearchOperators.get(node.name);
-		if (o != null && o.combinator) {
+		if (o?.combinator) {
 			node.args.forEach(function (a) { checkValues(a, type, column); });  // Bare literals under a combinator are validated too (as in Java typeTree).
 			return;
 		}
@@ -1251,7 +1251,7 @@
 		if (name == null || typeof fn !== "function")
 			throw new TypeError("JuneauViews.search.registerCustom requires a \"$\"-name and a predicate function.");
 		const n = String(name).trim();
-		if (n.charAt(0) !== "$" || ! isValidName(n) || ! IS_LETTER.test(n.charAt(1)))
+		if (! n.startsWith("$") || ! isValidName(n) || ! IS_LETTER.test(n.charAt(1)))
 			throw new TypeError("JuneauViews.search.registerCustom: invalid operator name '" + n + "'.");
 		if (SearchOperators.isBuiltin(n))
 			throw new TypeError("JuneauViews.search.registerCustom: '" + n + "' is a builtin operator.");
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-urlstate.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-urlstate.js
index e8a07d5ac9..8493658892 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-urlstate.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-urlstate.js
@@ -126,7 +126,7 @@
 	 * S3: Reading value (looking for top-level ',' or end); '(' / ')' adjust depth.
 	 * S4: Saw '\', next char is a value literal.
 	 */
-	function parseClauses(s) {
+	function parseClauses(s) { // NOSONAR javascript:S3776 -- URL state parsing; complexity is inherent
 		const out = [];
 		if (s == null || s === '') return out;
 		const S1 = 1, S2 = 2, S3 = 3, S4 = 4;
@@ -202,12 +202,12 @@
 	 *     as the prior {@code parseDirective} requirement that the token end with `)`).
 	 * S4: Discarding until the next top-level `;` after a malformed directive.
 	 */
-	function decodeState(str) {
+	function decodeState(str) { // NOSONAR javascript:S3776 -- URL state parsing; complexity is inherent
 		const state = { tab: null, filters: [], sort: null };
 		if (str == null) return state;
 		let raw = String(str);
 		if (raw.indexOf('%') >= 0) {
-			try { raw = decodeURIComponent(raw); } catch (e) { /* keep raw on a malformed %-sequence */ }
+			try { raw = decodeURIComponent(raw); } catch (e) { /* keep raw on a malformed %-sequence */ } // NOSONAR javascript:S2486 -- a malformed %-sequence keeps the raw text
 		}
 
 		const S1 = 1, S2 = 2, S3 = 3, S4 = 4;
@@ -323,7 +323,7 @@
 	function readFromSearch(search) {
 		if (search == null) return null;
 		let s = String(search);
-		if (s.charAt(0) === '?') s = s.substring(1);
+		if (s.startsWith('?')) s = s.substring(1);
 		if (s === '') return null;
 		const parts = s.split('&');
 		const prefix = STATE_PARAM + '=';
@@ -336,7 +336,7 @@
 	/** Rebuilds a search string, replacing (or dropping, when `encoded` is empty) the `state` param and keeping the rest. */
 	function buildSearch(existingSearch, encoded) {
 		let s = existingSearch == null ? '' : String(existingSearch);
-		if (s.charAt(0) === '?') s = s.substring(1);
+		if (s.startsWith('?')) s = s.substring(1);
 		const kept = [];
 		const prefix = STATE_PARAM + '=';
 		if (s !== '') {
@@ -359,11 +359,11 @@
 	 * address bar then stays clean while the user works, and only Copy link carries the state.
 	 */
 	function writeToAddressBar(history, location, state, opts) {
-		if (opts && opts.clean) return false;
+		if (opts?.clean) return false;
 		if (typeof history?.replaceState !== 'function' || ! location) return false;
 		const newSearch = buildSearch(location.search, encodeState(state));
 		const path = (location.pathname || '') + newSearch + (location.hash || '');
-		try { history.replaceState(history.state || null, '', path); } catch (e) { return false; }
+		try { history.replaceState(history.state || null, '', path); } catch (e) { return false; } // NOSONAR javascript:S2486 -- replaceState can throw (sandboxed frames); report false
 		return true;
 	}
 
@@ -383,7 +383,7 @@
 		try {
 			if (typeof navigatorLike?.clipboard?.writeText === 'function')
 				return Promise.resolve(navigatorLike.clipboard.writeText(text)).then(function () { return true; }, function () { return false; });
-		} catch (e) { /* fall through to the resolved-false path */ }
+		} catch (e) { /* fall through to the resolved-false path */ } // NOSONAR javascript:S2486 -- falls through to the resolved-false path
 		return Promise.resolve(false);
 	}
 
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js
index ea2b56cc8e..4b278b0263 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js
@@ -217,7 +217,7 @@
 		});
 		if (out.length === 0 && optsColumns) {
 			for (const [i, c] of optsColumns.entries()) {
-				if (!c || c.data == null || c.visible === false || c.orderable === false) continue;
+				if (c?.data == null || c.visible === false || c.orderable === false) continue;
 				out.push([i, "asc"]);
 				break;
 			}
@@ -291,7 +291,7 @@
 		const loc = window.location;
 		if (loc?.origin && loc.href && typeof URL === "function") {
 			try { return new URL(s, loc.href).origin === loc.origin; }
-			catch (e) { return false; }
+			catch (e) { return false; } // NOSONAR javascript:S2486 -- an unparseable URL is treated as cross-origin
 		}
 		return !/^([a-z][a-z0-9+.-]*:|[\\/]{2}|\/\\)/i.test(s);
 	}
@@ -462,7 +462,7 @@
 	 */
 	function hasBlankSubstitution(template, rowData) {
 		if (template == null) return false;
-		for (const m of String(template).matchAll(new RegExp(ROW_ACTION_TOKEN_RE, "g")))
+		for (const m of String(template).matchAll(new RegExp(ROW_ACTION_TOKEN_RE, "g"))) // NOSONAR javascript:S5852 -- ROW_ACTION_TOKEN_RE is linear ([^}] cannot match the closing brace); the template is developer-authored
 			if (isBlankToken(rowData ? rowData[m[1]] : undefined)) return true;
 		return false;
 	}
@@ -568,7 +568,7 @@
 	/** JSON.parse guarded against a null/blank/malformed body - returns the parsed value or null, never throws. */
 	function parseJsonSafe(text) {
 		if (text == null || text === "") return null;
-		try { return JSON.parse(text); } catch (e) { return null; }
+		try { return JSON.parse(text); } catch (e) { return null; } // NOSONAR javascript:S2486 -- malformed JSON deliberately yields null (documented parseJsonSafe contract)
 	}
 
 	/**
@@ -680,7 +680,7 @@
 	// NOSONAR javascript:S3504 -- the `var` keyword is load-bearing here, not an oversight; a05 of that test
 	// asserts on the literal string "var MIN_POLL_INTERVAL_MS = 5000;", so modernizing this declaration breaks
 	// the parity contract rather than tidying it.
-	var MIN_POLL_INTERVAL_MS = 5000;
+	var MIN_POLL_INTERVAL_MS = 5000; // NOSONAR javascript:S3504 -- `var` is load-bearing: a test pins this exact declaration text (see above)
 
 	/** Clamps a declared poll interval up to {@link #MIN_POLL_INTERVAL_MS} (mirrors the server-side clamp). */
 	function clampPollInterval(ms) {
@@ -787,7 +787,7 @@
 				let html = data == null ? "" : String(data);
 				if (renderer?.display) {
 						try { html = renderer.display(data, rowData, meta); }
-						catch (e) { html = data == null ? "" : String(data); }
+						catch (e) { html = data == null ? "" : String(data); } // NOSONAR javascript:S2486 -- a throwing renderer falls back to the escaped raw value
 						if (html == null) html = "";
 						else html = String(html);
 					}
@@ -802,8 +802,8 @@
 	function escapingTextRender(data, type) {
 		if (type && type !== "display") return data;
 		if (data == null) return "";
-		return String(data).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
-			.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
+		return String(data).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;")
+			.replaceAll('"', "&quot;").replaceAll("'", "&#39;");
 	}
 
 	/**
@@ -820,7 +820,7 @@
 	function appendRendererClass(def, renderer, meta) {
 		if (!renderer || typeof renderer["class"] !== "function") return;
 		let cls;
-		try { cls = renderer["class"](meta); } catch (e) { return; }
+		try { cls = renderer["class"](meta); } catch (e) { return; } // NOSONAR javascript:S2486 -- a throwing class() renderer means no CSS class
 		if (cls == null) return;
 		cls = String(cls).trim();
 		if (!cls) return;
@@ -1058,7 +1058,7 @@
 	 * case) is a no-op.  Pure: mutates the passed `opts` and touches no DOM/jQuery.
 	 */
 	function applyNestedScope(opts, scope) {
-		if (!opts || !opts.ajax || !scope || !scope.param) return;
+		if (!opts?.ajax || !scope?.param) return;
 		const parentId = function () {
 			const pid = typeof scope.parentId === "function" ? scope.parentId() : scope.parentId;
 			return pid != null && String(pid) !== "" ? pid : null;
@@ -1133,7 +1133,7 @@
 	 * of focus leaving the control entirely).
 	 */
 	// NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
-	function buildPageSizeMenu(ctx) {
+	function buildPageSizeMenu(ctx) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		const wrap = document.createElement("span");
 		wrap.className = "juneau-view-pagingpill-menuwrap";
 
@@ -1186,7 +1186,7 @@
 			menuEl.hidden = false;
 			btn.setAttribute("aria-expanded", "true");
 			const idx = indexOfSelected();
-			options[idx >= 0 ? idx : 0].el.focus();
+			options[Math.max(idx, 0)].el.focus();
 		}
 
 		function closeMenu(returnFocusToButton) {
@@ -1334,7 +1334,7 @@
 			const header = typeof col.header === "function" ? col.header() : null;
 			if (!header) return;
 			const def = (ctx.optsColumns || [])[col.index()];
-			const isSynthetic = !def || def.data == null;
+			const isSynthetic = def?.data == null;
 			const hidden = def?.visible === false;
 			if (hidden) return;
 			if (!isSynthetic && def.orderable !== false)
@@ -1439,7 +1439,7 @@
 		const def = (ctx?.optsColumns || [])[col.index()];
 		const dataName = def?.data != null ? String(def.data) : null;
 		if (dataName == null) return null;
-		const cols = (ctx?.viewDef && ctx.viewDef.columns) || [];
+		const cols = ctx?.viewDef?.columns || [];
 		for (const c of cols) {
 			if (c?.data != null && String(c.data) === dataName) return c.search || null;
 		}
@@ -1466,7 +1466,7 @@
 	 */
 	function isClientFiltered(ctx) {
 		if (typeof ctx?.clientFiltered === "boolean") return ctx.clientFiltered;
-		return !(ctx?.viewDef && ctx.viewDef.dataMode === "server");
+		return ctx?.viewDef?.dataMode !== "server";
 	}
 
 	/** Whether the column's own `search.operators` list offers `$regex` (S8: a shared link cannot smuggle one in). */
@@ -1685,7 +1685,7 @@
 			const helpList = document.createElement("div");
 			helpList.className = "juneau-view-col-search-popover-help";
 			meta.operators.forEach(function (o) {
-				if (!o || !o.name) return;
+				if (!o?.name) return;
 				// D4: on a client-filtered table a custom operator is only offered once the page registered its predicate.
 				if (dsl && o.custom && !(typeof search?.hasCustom === "function" && search.hasCustom(o.name))) return;
 				const row = document.createElement("div");
@@ -2151,7 +2151,7 @@
 	 * {@code WORK-J0516}.
 	 */
 	function isProtocolRelativeUrl(t) {
-		return t.replace(/[\t\r\n]/g, "").replace(/\\/g, "/").charAt(1) === "/";
+		return t.replaceAll(/[\t\r\n]/g, "").replaceAll("\\", "/").charAt(1) === "/";
 	}
 
 	/**
@@ -2180,9 +2180,9 @@
 			return false;
 		if (lower.startsWith("http://") || lower.startsWith("https://") || lower.startsWith("mailto:"))
 			return true;
-		if (t.charAt(0) === "#")
+		if (t.startsWith("#"))
 			return true;
-		if (t.charAt(0) === "/" || t.charAt(0) === "\\")
+		if (t.startsWith("/") || t.startsWith("\\"))
 			return !isProtocolRelativeUrl(t);
 		return lower.indexOf(":") < 0;
 	}
@@ -2294,14 +2294,14 @@
 		let parsed;
 		try {
 			parsed = new Parser().parseFromString("<div>" + String(html) + "</div>", "text/html");
-		} catch (e) {
+		} catch (e) { // NOSONAR javascript:S2486 -- parser failure is reported through the unsupported flag
 			return { doc: doc, wrap: null, unsupported: true };
 		}
 		return { doc: doc, wrap: parsed?.body?.firstChild || null, unsupported: false };
 	}
 
 	// NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
-	function fillMarkdownSlot(el, html) {
+	function fillMarkdownSlot(el, html) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		clearElementChildren(el);
 		if (html == null || html === "") return;
 		const src = String(html);
@@ -2378,9 +2378,9 @@
 			return false;
 		if (lower.startsWith("http://") || lower.startsWith("https://"))
 			return true;
-		if (t.charAt(0) === "/" || t.charAt(0) === "\\")
+		if (t.startsWith("/") || t.startsWith("\\"))
 			return !isProtocolRelativeUrl(t);
-		return t.charAt(0) !== "#" && lower.indexOf(":") < 0;
+		return !t.startsWith("#") && lower.indexOf(":") < 0;
 	}
 
 	/** Copies `name` from `from` to `to` when its value is an integer in [min,max].  Rejects everything else. */
@@ -2583,7 +2583,7 @@
 		const m = mergeRenderMeta(meta, href);
 		let html;
 		try { html = renderer.display(value, fields, m); }
-		catch (e) {
+		catch (e) { // NOSONAR javascript:S2486 -- a throwing renderer falls back to plain text
 			slot.textContent = value;
 			return;
 		}
@@ -2598,7 +2598,7 @@
 	}
 
 	function parseDetailFieldRenderMeta(slot) {
-		const metaRaw = slot.getAttribute("data-juneau-field-render-meta");
+		const metaRaw = slot.getAttribute("data-juneau-field-render-meta"); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 		if (!metaRaw) return {};
 		try {
 			const parsed = JSON.parse(metaRaw);
@@ -2613,15 +2613,15 @@
 	// row-detail panel, but reachable from server-rendered markup or a test double), and is read via
 	// getAttribute rather than assumed to expose the full `.dataset` API.
 	function paintDetailFieldSlot(slot, map) {
-		const key = slot.getAttribute("data-juneau-field");
+		const key = slot.getAttribute("data-juneau-field"); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 		const value = Object.hasOwn(map, key) ? scalarFieldValue(map[key]) : "";
-		const renderId = slot.getAttribute("data-juneau-field-render");
+		const renderId = slot.getAttribute("data-juneau-field-render"); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 		if (renderId) {
 			const meta = parseDetailFieldRenderMeta(slot);
-			const href = slot.getAttribute("data-juneau-field-render-href");
+			const href = slot.getAttribute("data-juneau-field-render-href"); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 			fillRenderSlot(slot, value, renderId, meta, href, map);
 		} else {
-			const fmt = slot.getAttribute("data-juneau-field-format");
+			const fmt = slot.getAttribute("data-juneau-field-format"); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 			if (fmt === "markdown")
 				fillMarkdownSlot(slot, value);
 			else if (fmt === "sanitizedHtml")
@@ -2633,9 +2633,9 @@
 
 	// NOSONAR javascript:S7761 -- same caller-supplied-DOM rationale as paintDetailFieldSlot above.
 	function paintDetailTitleSlot(el, map, allow) {
-		const tmpl = el.getAttribute("data-juneau-detail-title-template") || "";
+		const tmpl = el.getAttribute("data-juneau-detail-title-template") || ""; // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 		el.textContent = tmpl.replace(/\{(\w+)\}/g, function (_, key) {
-			if (!allow || !allow.has(key)) return "";
+			if (!allow?.has(key)) return "";
 			return Object.hasOwn(map, key) ? scalarFieldValue(map[key]) : "";
 		});
 	}
@@ -2656,7 +2656,7 @@
 	 * `data-juneau-title-fields` allowlist (copied from the template at expand time) are substituted.
 	 */
 	function fillDetailSlots(root, fields) {
-		if (!root || !root.querySelectorAll) return;
+		if (!root?.querySelectorAll) return;
 		const map = fields && typeof fields === "object" ? fields : {};
 		for (const slot of root.querySelectorAll("[data-juneau-field]")) paintDetailFieldSlot(slot, map);
 		const allow = titleFieldAllowlist(root);
@@ -2687,7 +2687,7 @@
 	 * Paints an action's message into the first field slot of the section that owns that action button
 	 * (in-tab Diagnose findings).  TEXT slots use textContent; markdown slots use fillMarkdownSlot.
 	 */
-	function paintActionMessageIntoDetail(tr, actionId, message) {
+	function paintActionMessageIntoDetail(tr, actionId, message) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		const panel = tr?._juneauDetailPanel;
 		if (!panel || typeof panel.querySelectorAll !== "function") return;
 		const want = String(actionId == null ? "" : actionId);
@@ -2824,7 +2824,7 @@
 	}
 
 	function buildRibbonStrip(items, opts) {
-		if (!items || !items.length) return null;
+		if (!items?.length) return null;
 		const o = opts || {};
 		const activeIndex = o.activeIndex == null ? 0 : o.activeIndex;
 		const strip = document.createElement("div");
@@ -2909,7 +2909,7 @@
 	function probeIsDisabled(el) {
 		if (!el) return true;
 		const cn = classStringOf(el);
-		if (cn && cn.split(/\s+/).includes("is-disabled")) return true;
+		if (cn?.split(/\s+/).includes("is-disabled")) return true;
 		return el.getAttribute ? el.getAttribute("aria-disabled") === "true" : false;
 	}
 
@@ -2939,7 +2939,7 @@
 	 * no probe is enabled).  Right/Down step forward, Left/Up step back - both skip disabled probes and wrap; Home/End
 	 * jump to the first/last ENABLED probe.  Pure - no DOM - so the arrow-key contract is unit-checkable.
 	 */
-	function probeTargetIndex(key, currentIndex, enabled) {
+	function probeTargetIndex(key, currentIndex, enabled) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		const n = enabled.length;
 		if (n === 0 || enabled.indexOf(true) < 0) return -1;
 		if (key === "Home") {
@@ -2962,6 +2962,9 @@
 		return -1;
 	}
 
+	/** Reads a probe's stable id attribute, or {@code null} when absent. */
+	function idOf(el) { return el?.getAttribute?.(PROBE_ID_ATTR) ?? null; }
+
 	/**
 	 * Enhances a server-painted probe group in place: wires radiogroup/radio roles, the roving tabindex, click and
 	 * arrow-key selection, and returns an imperative handle {group, getSelected(), select(idOrEl), repaint()}.
@@ -2981,7 +2984,6 @@
 		if (group.setAttribute) group.setAttribute("role", "radiogroup");
 
 		function probes() { return probesInGroup(group); }
-		function idOf(el) { return el?.getAttribute?.(PROBE_ID_ATTR) ?? null; }
 		function findById(id) {
 			if (id == null) return null;
 			for (const el of probes()) if (idOf(el) === id) return el;
@@ -2996,7 +2998,7 @@
 
 		// Repaints aria-checked (by identity) and the roving tabindex: the single tab stop is the selected probe, or the
 		// first enabled probe when nothing is selected.  Selection colour is chrome.css's job; this touches only state.
-		function paint() {
+		function paint() { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 			const list = probes();
 			const enabled = list.map(function (el) { return !probeIsDisabled(el); });
 			let tabStop = -1;
@@ -3216,7 +3218,7 @@
 	// back to dataset so both real DOM elements and those mocks are recognized uniformly.
 	function isActionRefPill(b) {
 		return b.getAttribute?.("role") === "button"
-			&& (b.hasAttribute ? b.hasAttribute("data-juneau-pill") : b.dataset.juneauPill != null);
+			&& (b.hasAttribute ? b.hasAttribute("data-juneau-pill") : b.dataset.juneauPill != null); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 	}
 
 	function setPillDisabledVisual(b, enabled) {
@@ -3231,7 +3233,7 @@
 
 	/** Enables or disables ActionRef buttons; SafeAction.COLLAPSE is never touched. */
 	function setActionRefEnabled(root, enabled) {
-		if (!root || !root.querySelectorAll) return;
+		if (!root?.querySelectorAll) return;
 		for (const b of root.querySelectorAll("[data-juneau-action]")) {
 			b.disabled = !enabled;
 			if (isActionRefPill(b)) setPillDisabledVisual(b, enabled);
@@ -3240,7 +3242,7 @@
 
 	/** Hides ActionRef buttons (404/500 / contract-fail closed) while leaving COLLAPSE in place. */
 	function hideActionRefs(root) {
-		if (!root || !root.querySelectorAll) return;
+		if (!root?.querySelectorAll) return;
 		const buttons = root.querySelectorAll("[data-juneau-action]");
 		for (const b of buttons) {
 			b.disabled = true;
@@ -3292,7 +3294,7 @@
 
 	/** Whether one rule matches the expand payload's `fields` map (a matching rule leaves the action offered). */
 	function actionRuleMatches(rule, map) {
-		if (!rule || rule.field == null) return true;
+		if (rule?.field == null) return true;
 		// FAIL CLOSED on a field the payload does not carry.  RowDetailDef.validate rejects a rule keyed on an
 		// undeclared field at startup, so an absent key here is a broken expand contract rather than a state to
 		// interpret - including for `absent`, which tests a field that came back empty, not one that never came.
@@ -3323,7 +3325,7 @@
 	function actionDescNodeFor(btn) {
 		const parent = btn.parentNode;
 		if (!parent || typeof parent.querySelectorAll !== "function") return null;
-		const want = btn.getAttribute("data-juneau-action");
+		const want = btn.getAttribute("data-juneau-action"); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 		for (const n of parent.querySelectorAll("[" + ACTION_DESC_ATTR + "]"))
 			if (n.getAttribute(ACTION_DESC_ATTR) === want) return n;
 		return null;
@@ -3361,7 +3363,7 @@
 	 * fresh expand GET through the same path.
 	 */
 	function applyActionRefRules(root, fields) {
-		if (!root || !root.querySelectorAll) return;
+		if (!root?.querySelectorAll) return;
 		const map = fields && typeof fields === "object" ? fields : {};
 		for (const b of root.querySelectorAll("[" + ACTION_RULES_ATTR + "]")) {
 			const failing = firstFailingActionRule(parseActionRefRules(b), map);
@@ -3413,7 +3415,7 @@
 	 */
 	function firstFailingRowActionRule(action, rowData) {
 		const rules = action?.enabledWhen;
-		if (!rules || !rules.length) return null;
+		if (!rules?.length) return null;
 		const map = rowData && typeof rowData === "object" ? rowData : {};
 		return firstFailingActionRule(rules, map);
 	}
@@ -3563,7 +3565,7 @@
 		const tr = control.closest("tr.juneau-view-detail-row");
 		if (!tr) return;
 		const row = dt.row(tr);
-		if (!row || !row.length) return;
+		if (!row?.length) return;
 		if (row.child.isShown()) {
 			// Removal path 2/5: tear down enrolled regions + nested DataTables before hiding (their child-row
 			// DOM is about to be detached).  Region teardown ordered first, matching handleDetailSafeCollapseClick's
@@ -3603,7 +3605,7 @@
 		let collapsedAny = false;
 		openRows.forEach(function (tr) {
 			const row = dt.row(tr);
-			if (!row || !row.length || !row.child.isShown()) return;
+			if (!row?.length || !row.child.isShown()) return;
 			// Removal path 3/5: same region-teardown-first ordering as toggleDetailRow's own collapse branch.
 			if (tr._juneauDetailPanel) {
 				NS.regions?.teardownRegionsIn(tr._juneauDetailPanel);
@@ -3679,7 +3681,7 @@
 				const n = v ? Number.parseInt(String(v).trim(), 10) : Number.NaN;
 				if (! Number.isNaN(n)) return n;
 			}
-		} catch (e) { /* fall through to the fallback */ }
+		} catch (e) { /* fall through to the fallback */ } // NOSONAR javascript:S2486 -- best-effort lookup; the fallback below is the defined result
 		return fallback;
 	}
 
@@ -3713,7 +3715,7 @@
 	function focusFirstInLayer(rec) {
 		const f = focusablesIn(rec.el);
 		const target = f.length ? f[0] : rec.el;
-		if (typeof target?.focus === "function") { try { target.focus(); } catch (e) { /* ignore */ } }
+		if (typeof target?.focus === "function") { try { target.focus(); } catch (e) { /* ignore */ } } // NOSONAR javascript:S2486 -- focus restore is best-effort
 	}
 
 	/**
@@ -3761,14 +3763,14 @@
 	function dismissRemovedLayers(removed) {
 		for (const rec of removed.slice().reverse()) {
 			if (rec.detachOnPop && rec.el?.parentNode) rec.el.remove();
-			if (rec.onDismiss) { try { rec.onDismiss(); } catch (e) { /* ignore */ } }
+			if (rec.onDismiss) { try { rec.onDismiss(); } catch (e) { /* ignore */ } } // NOSONAR javascript:S2486 -- a throwing onDismiss hook must not block the pop
 		}
 	}
 
 	function restoreLayerFocus(target) {
 		if (typeof target?.focus !== "function") return;
 		if (typeof document !== "undefined" && typeof document.contains === "function" && !document.contains(target)) return;
-		try { target.focus(); } catch (e) { /* ignore */ }
+		try { target.focus(); } catch (e) { /* ignore */ } // NOSONAR javascript:S2486 -- focus restore is best-effort
 	}
 
 	function popLayer(el) {
@@ -3787,7 +3789,7 @@
 
 	function handleLayerTab(e) {
 		const top = topLayer();
-		if (! top || ! top.trapFocus) return;
+		if (!top?.trapFocus) return;
 		const f = focusablesIn(top.el);
 		if (! f.length) { e.preventDefault(); return; }
 		const first = f[0], last = f.at(-1);
@@ -3814,7 +3816,7 @@
 		// ONE document pointerdown: an outside click dismisses the TOP layer only, and only when it is light-dismiss.
 		document.addEventListener("pointerdown", function (e) {
 			const top = topLayer();
-			if (! top || ! top.lightDismiss) return;
+			if (!top?.lightDismiss) return;
 			const t = e.target;
 			if (t && top.el?.contains?.(t)) return;
 			popLayer();
@@ -3952,7 +3954,7 @@
 		const meta = popoverRenderMeta(spec);
 		let html;
 		try { html = renderer.display(value, rowData, meta); }
-		catch (e) {
+		catch (e) { // NOSONAR javascript:S2486 -- a throwing renderer falls back to plain text
 			paintPopoverTextValue(cell, value);
 			return;
 		}
@@ -3979,7 +3981,7 @@
 	}
 
 	function appendCellPopoverFieldRow(el, doc, f, rowData) {
-		if (!f || f.data == null) return;
+		if (f?.data == null) return;
 		const row = doc.createElement("div");
 		row.className = "jc-cell-popover-row";
 		const lab = doc.createElement("div");
@@ -4411,7 +4413,7 @@
 	// specifically documents a `null` (not `undefined`) return for a missing token; `dataset.juneauCsrf`
 	// would return `undefined` instead, changing this public function's documented contract.
 	function resolveCsrfToken(table) {
-		return table.getAttribute("data-juneau-csrf");
+		return table.getAttribute("data-juneau-csrf"); // NOSONAR javascript:S7761 -- the documented contract returns null (not undefined) for a missing token, which .dataset cannot
 	}
 
 	/** The per-table CSRF header-name override (`data-juneau-csrf-header`), else the framework default. */
@@ -4752,7 +4754,7 @@
 	 *   - 2xx + no typed body -> a bare success (the pre-416 behavior: redraw for an onSuccess=redraw action).
 	 */
 	// NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
-	function settleActionResponse(resp, action, table, tr, ctx) {
+	function settleActionResponse(resp, action, table, tr, ctx) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine, so complexity is inherent.
 		setRowInFlight(tr, false);   // EVERY terminal outcome clears the marker first - polling must always resume.
 		if (! resp) {
 			// T3: an absent response does not prove the write didn't land, so `unknown` never re-enables Confirm.
@@ -4777,7 +4779,7 @@
 			return;
 		}
 
-		readBodyText(resp).then(function (text) {
+		readBodyText(resp).then(function (text) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 			// Async-SSE-job feature: an ASYNC action's start POST returns a job pointer (not a terminal result).  The in-flight
 			// marker was ALREADY cleared above (first line of settle), so table polling has resumed BEFORE the long
 			// job runs - the job then uses the DISTINCT job-running affordance (data-juneau-job), never
@@ -4860,7 +4862,7 @@
 	/** Reads a fetch Response body as text, defensively (a stubbed/absent body resolves to "" rather than throwing). */
 	function readBodyText(resp) {
 		if (typeof resp?.text === "function") {
-			try { return Promise.resolve(resp.text()); } catch (e) { return Promise.resolve(""); }
+			try { return Promise.resolve(resp.text()); } catch (e) { return Promise.resolve(""); } // NOSONAR javascript:S2486 -- an unreadable body deliberately reads as empty
 		}
 		return Promise.resolve("");
 	}
@@ -4901,7 +4903,7 @@
 					mergedViaDt = true;
 				}
 			}
-		} catch (e) { mergedViaDt = false; }
+		} catch (e) { mergedViaDt = false; } // NOSONAR javascript:S2486 -- failure is recorded in mergedViaDt and handled by the mergeRow fallback
 		if (! mergedViaDt && typeof ctx?.mergeRow === "function") ctx.mergeRow(tr, rowData);
 		if (tr?.dataset) tr.dataset.juneauRowMerged = "1";
 	}
@@ -5232,7 +5234,7 @@
 		}
 		fetch(resolved.url, { method: "GET", credentials: "same-origin", headers: { "Accept": "application/json" } })
 			.then(function (resp) {
-				if (! resp || ! resp.ok) {
+				if (! resp?.ok) {
 					// A non-2xx on the read-only confirmation fetch is itself a visible transport refusal - the
 					// modal never opens optimistically on a boundary rejection.
 					const boundaryReason = (typeof resp?.headers?.get === "function")
@@ -5522,7 +5524,7 @@
 	function buildSelectFormControl(f) {
 		const control = document.createElement("select");
 		for (const o of (f.options || [])) {
-			if (! o || o.value == null) continue;
+			if (o?.value == null) continue;
 			const opt = document.createElement("option");
 			opt.value = String(o.value);
 			opt.textContent = o.label != null ? String(o.label) : String(o.value);
@@ -5745,7 +5747,7 @@
 		const top = topLayer();
 		const el = top?.el;
 		const host = (el?.querySelector?.(".juneau-view-dialog")) || el;
-		if (! host || ! host.appendChild) return;
+		if (! host?.appendChild) return;
 		let banner = host.querySelector ? host.querySelector("." + cls) : null;
 		if (! banner) {
 			banner = document.createElement("div");
@@ -5769,7 +5771,7 @@
 		// A sectioned form and a flat one are mutually exclusive on the server (FormDef.validate rejects both), so
 		// sections win here without needing to reconcile the two.
 		if (form.sections?.length) { appendSectionedDialogForm(dialog, form, table, tr, ctx, seq, childCatalog); return; }
-		if (!form.fields || !form.fields.length) return;
+		if (!form.fields?.length) return;
 		const wrap = document.createElement("div");
 		wrap.className = "juneau-view-dialog-form";
 		wrap.dataset.testid = "dialog-form";
@@ -5808,8 +5810,8 @@
 		bindControlValidation(dialog, control);
 	}
 
-	function appendDialogFormRow(host, dialog, f, table, tr, ctx, seq, childCatalog) {
-		if (!f || f.name == null || String(f.name) === "") return;
+	function appendDialogFormRow(host, dialog, f, table, tr, ctx, seq, childCatalog) { // NOSONAR javascript:S107 -- every parameter is dialog-build context threaded through unchanged; collapsing them into an object would only move the count
+		if (f?.name == null || String(f.name) === "") return;
 		const type = (f.type == null || f.type === "") ? "text" : String(f.type);
 		if (! isTypedFormInputType(type)) return;
 		const row = document.createElement("div");
@@ -5847,7 +5849,7 @@
 
 		const items = [];
 		for (const s of form.sections) {
-			if (!s || s.id == null || String(s.id) === "") continue;
+			if (s?.id == null || String(s.id) === "") continue;
 			const pane = document.createElement("div");
 			pane.className = "juneau-view-dialog-form-section";
 			pane.dataset.juneauFormSection = String(s.id);
@@ -5880,7 +5882,7 @@
 	 */
 	function revealDialogSectionFor(el) {
 		const pane = (typeof el?.closest === "function") ? el.closest("[data-juneau-form-section]") : null;
-		if (! pane || ! pane.hidden) return;
+		if (! pane?.hidden) return;
 		const wrap = (typeof pane.closest === "function") ? pane.closest(".juneau-view-dialog-form") : null;
 		const activate = wrap?._juneauActivateSection ?? null;
 		if (typeof activate === "function") activate(pane.dataset.juneauFormSection);
@@ -5906,12 +5908,12 @@
 	function focusFirstInvalidControl(el) {
 		revealDialogSectionFor(el);
 		if (typeof el.focus === "function") {
-			try { el.focus(); } catch (e) { /* ignore */ }
+			try { el.focus(); } catch (e) { /* ignore */ } // NOSONAR javascript:S2486 -- focus is best-effort
 		}
 	}
 
 	function validateDialogForm(dialog, fromConfirm) {
-		if (!dialog || !dialog.querySelectorAll) return true;
+		if (!dialog?.querySelectorAll) return true;
 		const nodes = dialog.querySelectorAll("[data-juneau-form-field]");
 		let firstInvalid = null;
 		for (const el of nodes) {
@@ -5931,7 +5933,7 @@
 			return "Must be at most " + max + " characters.";
 		if (pat) {
 			let re = null;
-			try { re = new RegExp(pat); } catch (e) { re = null; }   // FAIL-OPEN on a Java-only pattern
+			try { re = new RegExp(pat); } catch (e) { re = null; }   // FAIL-OPEN on a Java-only pattern // NOSONAR javascript:S2486 -- fail-open on a pattern the browser regex engine cannot compile (Java-only syntax)
 			if (re && ! re.test(value)) return "Value is not in the expected format.";
 		}
 		return null;
@@ -6003,7 +6005,7 @@
 
 	function collectDialogFormFields(dialog) {
 		const out = {};
-		if (!dialog || !dialog.querySelectorAll) return out;
+		if (!dialog?.querySelectorAll) return out;
 		for (const el of dialog.querySelectorAll("[data-juneau-form-field]")) {
 			const entry = collectDialogFormFieldEntry(el);
 			if (entry) out[entry.name] = entry.value;
@@ -6348,9 +6350,9 @@
 		btn.addEventListener("click", function () {
 			const clip = (typeof navigator === "undefined") ? null : navigator?.clipboard;
 			if (typeof clip?.writeText !== "function") return;
-			try {
+			try { // NOSONAR javascript:S4822 -- the try only guards a synchronous throw from clipboard.writeText; the rejection is handled by the .catch inside
 				Promise.resolve(clip.writeText(value)).catch(function () { /* denied - the <pre> stays selectable */ });
-			} catch (e) { /* a copy affordance never throws */ }
+			} catch (e) { /* a copy affordance never throws */ } // NOSONAR javascript:S2486 -- a copy affordance never throws
 		});
 		return btn;
 	}
@@ -6458,7 +6460,7 @@
 		}
 		fetch(url, { method: "GET", credentials: "same-origin", headers: { "Accept": "application/json" } })
 			.then(function (resp) {
-				if (! resp || ! resp.ok) { resultFormTerminal(RESULT_FORM_UNAVAILABLE_NOTICE); return null; }
+				if (! resp?.ok) { resultFormTerminal(RESULT_FORM_UNAVAILABLE_NOTICE); return null; }
 				return readBodyText(resp).then(function (text) {
 					const payload = parseJsonSafe(text);
 					if (! payload) { resultFormTerminal(RESULT_FORM_UNAVAILABLE_NOTICE); return; }
@@ -6779,7 +6781,7 @@
 		}
 		if (ctx._jobSources) {
 			ctx._jobSources.forEach(function (es) {
-				try { if (es?.close) es.close(); } catch (e) { /* already closed */ }
+				try { if (es?.close) es.close(); } catch (e) { /* already closed */ } // NOSONAR javascript:S2486 -- best-effort cleanup; the source may already be closed
 			});
 			ctx._jobSources.clear();
 		}
@@ -6801,11 +6803,11 @@
 		}
 		// DT1 table-overflow-wrap discipline: disconnect the scroll-region ResizeObserver before destroy (re-stamped on reconstruct).
 		if (ctx._scrollRegionObserver) {
-			try { ctx._scrollRegionObserver.disconnect(); } catch (e) { /* already gone */ }
+			try { ctx._scrollRegionObserver.disconnect(); } catch (e) { /* already gone */ } // NOSONAR javascript:S2486 -- best-effort cleanup; the observer may already be gone
 			ctx._scrollRegionObserver = null;
 		}
 		if (ctx.dataTable) {
-			try { ctx.dataTable.destroy(); } catch (e) { /* already destroyed */ }
+			try { ctx.dataTable.destroy(); } catch (e) { /* already destroyed */ } // NOSONAR javascript:S2486 -- best-effort cleanup; the table may already be destroyed
 			ctx.dataTable = null;
 		}
 		stripGeneratedDom(table);
@@ -6896,10 +6898,10 @@
 		box.className = TABLE_SCROLL_CLASS;
 		// NOSONAR javascript:S7768 -- `table.before(box)` would be equivalent, but the Node test-harness DOM
 		// shim (views-dom-shim.cjs) implements insertBefore/appendChild only, not `.before()`.
-		parent.insertBefore(box, table);
+		parent.insertBefore(box, table); // NOSONAR javascript:S7768 -- the Node test-harness DOM shim implements insertBefore/appendChild only, not .before()
 		box.appendChild(table);
 		if (ctx?.dataTable?.columns) {
-			try { ctx.dataTable.columns.adjust(); } catch (e) { /* not yet drawable */ }
+			try { ctx.dataTable.columns.adjust(); } catch (e) { /* not yet drawable */ } // NOSONAR javascript:S2486 -- best-effort resize; the table may not be drawable yet
 		}
 	}
 
@@ -6909,14 +6911,14 @@
 	 * overflow box.  No-op on the DT2 path (there is no JS-inserted wrap to remove).
 	 */
 	function unwrapTableScroll(table) {
-		if (!table || !table.parentNode) return;
+		if (!table?.parentNode) return;
 		const box = table.parentNode;
 		if (!box.className || (" " + box.className + " ").indexOf(" " + TABLE_SCROLL_CLASS + " ") < 0) return;
 		const grandparent = box.parentNode;
 		if (!grandparent) return;
 		// NOSONAR javascript:S7768 -- `box.before(table)` would be equivalent, but the Node test-harness DOM
 		// shim (views-dom-shim.cjs) implements insertBefore/appendChild only, not `.before()`.
-		grandparent.insertBefore(table, box);
+		grandparent.insertBefore(table, box); // NOSONAR javascript:S7768 -- the Node test-harness DOM shim implements insertBefore/appendChild only, not .before()
 		box.remove();
 	}
 
@@ -6953,7 +6955,7 @@
 		};
 		recheck();
 		if (ctx?._scrollRegionObserver) {
-			try { ctx._scrollRegionObserver.disconnect(); } catch (e) { /* already gone */ }
+			try { ctx._scrollRegionObserver.disconnect(); } catch (e) { /* already gone */ } // NOSONAR javascript:S2486 -- best-effort cleanup; the observer may already be gone
 			ctx._scrollRegionObserver = null;
 		}
 		if (typeof window.ResizeObserver === "function") {
@@ -7138,7 +7140,7 @@
 	}
 
 	/** Live Page Tab id for the shareable URL, or {@code null} when the page has no tab signal. */
-	function readLiveShareTab() {
+	function readLiveShareTab() { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		const meta = typeof document.querySelector === "function"
 			? document.querySelector('meta[name="page-tab"]') : null;
 		if (meta?.getAttribute) {
@@ -7173,7 +7175,7 @@
 			dt.columns().every(function () {
 				const col = this; // NOSONAR javascript:S7740 -- DataTables columns().every() binds `this` to the column API; an arrow function cannot receive it
 				const def = cols[col.index()];
-				if (!def || def.data == null) return;
+				if (def?.data == null) return;
 				const expr = getColumnExpr(ctx, col);
 				if (String(expr) !== "") filters.push({ column: String(def.data), expr: String(expr) });
 			});
@@ -7199,8 +7201,8 @@
 	 * DSL column it must also pass the strict server-parity gate (WORK-J0612 D3), which skips a `$regex` the column
 	 * does not offer or one over the server's pattern-length cap (S8), so a crafted link cannot hang the tab.
 	 */
-	function applyShareableOpenState(table, ctx, state) {
-		if (!state || !ctx || !ctx.dataTable) return;
+	function applyShareableOpenState(table, ctx, state) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
+		if (!state || !ctx?.dataTable) return;
 		const U = NS.urlState;
 		if (U && typeof U.isEmptyState === "function" && U.isEmptyState(state)) return;
 
@@ -7214,27 +7216,27 @@
 			if (typeof document.querySelectorAll === "function") {
 				const candidates = document.querySelectorAll('[role="tab"][data-juneau-strip-tab]');
 				for (const c of candidates) {
-					if (c?.dataset && c.dataset.juneauStripTab === tabId) { btn = c; break; }
+					if (c?.dataset?.juneauStripTab === tabId) { btn = c; break; }
 				}
 			}
 			if (btn && typeof btn.click === "function") {
-				try { btn.click(); } catch (e) { /* host tab strip unavailable */ }
+				try { btn.click(); } catch (e) { /* host tab strip unavailable */ } // NOSONAR javascript:S2486 -- the host tab strip may be unavailable
 			}
 			try {
 				if (typeof CustomEvent === "function")
 					document.dispatchEvent(new CustomEvent("juneau:share-tab", { detail: { tab: tabId } }));
-			} catch (e2) { /* CustomEvent unavailable in the harness */ }
+			} catch (error_) { /* CustomEvent unavailable in the harness */ } // NOSONAR javascript:S2486 -- CustomEvent is unavailable in some harnesses
 		}
 
 		const cols = ctx.optsColumns || [];
 		const search = NS.search;
 		const filters = state.filters || [];
 		for (const f of filters) {
-			if (!f || f.column == null || f.expr == null || String(f.expr) === "") continue;
+			if (f?.column == null || f.expr == null || String(f.expr) === "") continue;
 			const expr = String(f.expr);
 			let colIdx = -1;
 			for (let c = 0; c < cols.length; c++) {
-				if (cols[c] && cols[c].data != null && String(cols[c].data) === String(f.column)) {
+				if (cols[c]?.data != null && String(cols[c].data) === String(f.column)) {
 					colIdx = c;
 					break;
 				}
@@ -7257,7 +7259,7 @@
 		if (state.sort?.column != null && state.sort.dir != null) {
 			let sortIdx = -1;
 			for (let c = 0; c < cols.length; c++) {
-				if (cols[c] && cols[c].data != null && String(cols[c].data) === String(state.sort.column)) {
+				if (cols[c]?.data != null && String(cols[c].data) === String(state.sort.column)) {
 					sortIdx = c;
 					break;
 				}
@@ -7310,8 +7312,8 @@
 	 * called from {@code constructTable} on first init AND every Apply rebuild, and a second call never duplicates
 	 * the button.
 	 */
-	function mountCopyLinkButton(table, ctx, toolbarRow) {
-		if (!ctx || !ctx.viewDef) return;
+	function mountCopyLinkButton(table, ctx, toolbarRow) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
+		if (!ctx?.viewDef) return;
 		if (!isCopyLinkVisible(table, ctx.viewDef)) return;
 		// No URL-state module -> nothing to copy; don't render a button that can only fail.
 		if (!NS.urlState || typeof NS.urlState.buildShareUrl !== "function") return;
@@ -7351,8 +7353,8 @@
 	 * Wires live address-bar sync (T17/T18) and open precedence (T19) for the primary table. Nested / non-primary
 	 * tables are a no-op. Idempotent per table.
 	 */
-	function wireShareableUrlState(table, ctx) {
-		if (!table || !ctx || !ctx.dataTable) return;
+	function wireShareableUrlState(table, ctx) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
+		if (!table || !ctx?.dataTable) return;
 		if (!isShareablePrimaryTable(table, ctx.viewDef)) return;
 		if (table.dataset?.juneauUrlStateWired === "1") return;
 		if (table.dataset) table.dataset.juneauUrlStateWired = "1";
@@ -7373,7 +7375,7 @@
 		}
 
 		const onLiveChange = function (e) {
-			if (e && e.target && e.target !== table) return;
+			if (e?.target && e.target !== table) return;
 			syncShareableUrlState(table, ctx);
 		};
 		if (typeof ctx.dataTable.on === "function") {
@@ -7580,7 +7582,7 @@
 		let viewDef;
 		try {
 			viewDef = JSON.parse(sidecar.textContent);
-		} catch (e) {
+		} catch (e) { // NOSONAR javascript:S2486 -- the failure is reported via error() just below
 			error("Juneau nested table '" + id + "': malformed nested sidecar; refusing to init.");
 			renderBanner(table, "Juneau nested table '" + id + "': malformed configuration.");
 			return;
@@ -7656,7 +7658,7 @@
 	function adjustNestedColumns(table) {
 		const ctx = table?.__juneauCtx;
 		if (ctx?.dataTable?.columns)
-			try { ctx.dataTable.columns.adjust(); } catch (e) { /* not yet drawable */ }
+			try { ctx.dataTable.columns.adjust(); } catch (e) { /* not yet drawable */ } // NOSONAR javascript:S2486 -- best-effort resize; the table may not be drawable yet
 	}
 
 	/**
@@ -7707,7 +7709,7 @@
 			const t = tables[i];
 			const ctx = t.__juneauCtx;
 			if (ctx) {
-				try { teardownTable(t, ctx); } catch (e) { /* already gone */ }
+				try { teardownTable(t, ctx); } catch (e) { /* already gone */ } // NOSONAR javascript:S2486 -- best-effort teardown; the table may already be gone
 				t.__juneauCtx = null;
 			}
 			t.removeAttribute(NESTED_INIT_ATTR);
@@ -7774,7 +7776,7 @@
 		let viewDef;
 		try {
 			viewDef = JSON.parse(sidecar.textContent);
-		} catch (e) {
+		} catch (e) { // NOSONAR javascript:S2486 -- the failure is reported via error() just below
 			error("Juneau view '" + id + "': malformed JSON sidecar; refusing to init.");
 			renderBanner(table, "Juneau view '" + id + "': malformed configuration.");
 			return null;
@@ -7821,7 +7823,7 @@
 		const $ = window.jQuery;
 		if ($?.fn?.dataTable?.isDataTable(table)) return;
 		const id = table.dataset.juneauView;
-		if (!viewDef || viewDef.contractVersion !== JUNEAU_VIEW_CONTRACT_VERSION) {
+		if (viewDef?.contractVersion !== JUNEAU_VIEW_CONTRACT_VERSION) {
 			const m = "Juneau view '" + id + "': contract version mismatch (page='" +
 				(viewDef?.contractVersion) + "', runtime='" + JUNEAU_VIEW_CONTRACT_VERSION
 				+ "'). Refusing to init - reload to clear a stale cached script.";
@@ -7898,7 +7900,7 @@
 			let effective = resolveEffectiveColumns(viewDef, viewSaved);
 			let effectiveViewDef = viewDef;
 			if (draft && tabOn("sort") && draft.sort?.length)
-				effectiveViewDef = Object.assign({}, viewDef, { defaultOrder: defaultOrderFromSort(draft.sort) });
+				effectiveViewDef = { ...viewDef, defaultOrder: defaultOrderFromSort(draft.sort) };
 			if (draft && tabOn("search") && typeof NS.config?.applySearchMembershipToColumns === "function")
 				effective = NS.config.applySearchMembershipToColumns(effective, draft.search);
 			// Routed through the NS.init export (rather than the closed-over `buildTable` reference directly) so
@@ -7938,7 +7940,7 @@
 		const fetchFn = window.fetch || fetch;
 		return fetchFn(url, { credentials: "same-origin", headers: { Accept: "application/json" } })
 			.then(function (resp) {
-				if (!resp || !resp.ok)
+				if (!resp?.ok)
 					throw new Error("Juneau slot: envelope GET failed (" + (resp?.status) + ").");
 				return resp.text();
 			})
@@ -7946,7 +7948,7 @@
 				try {
 					return JSON.parse(text);
 				} catch (e) {
-					throw new Error("Juneau slot: malformed JSON envelope.");
+					throw new Error("Juneau slot: malformed JSON envelope.", { cause: e });
 				}
 			});
 	}
@@ -7967,7 +7969,7 @@
 		});
 	}
 
-	function paintSlotTable(slot, envelope) {
+	function paintSlotTable(slot, envelope) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		if (!envelope || typeof envelope !== "object")
 			throw new Error("Juneau slot: envelope is not an object.");
 		if (envelope.contractVersion !== JUNEAU_SLOT_CONTRACT_VERSION) {
@@ -8053,7 +8055,7 @@
 			table.dataset.juneauCsrfHeader = header;
 	}
 
-	function buildSlotTableEl(viewDef, selection, detail, rows) {
+	function buildSlotTableEl(viewDef, selection, detail, rows) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		const table = document.createElement("table");
 		table.id = viewDef.id;
 		table.dataset.juneauView = viewDef.id;
@@ -8373,11 +8375,18 @@
 	 * DOMContentLoaded scan below - DataTables mis-sizes columns initialized inside a display:none panel.
 	 * A standalone page with no page-id ancestor is unaffected - every one of its tables is still inited
 	 * exactly as before.  HTML-slot nav stamps `data-juneau-page` on the nav itself, not around tables.
+	 *
+	 * Also skipped: a table inside a [data-juneau-slot-table] wrapper (built by `mountTableSlot`).  Slot tables carry no
+	 * `juneau-view:<id>` sidecar - the card envelope is their definition - and `paintSlotTable` already inits them, so
+	 * a scan that raced the slot mount would only log "missing JSON sidecar" for a table that is being set up correctly.
 	 */
 	function initAll() {
 		const tables = document.querySelectorAll("table[data-juneau-view]");
 		Array.prototype.forEach.call(tables, function (t) {
 			if (t.closest?.("[data-juneau-page]")) return;
+			// Skip tables built into a slot by mountTableSlot: they carry no sidecar (the envelope is their definition)
+			// and paintSlotTable already inits them, so a scan that races the slot mount would only log a missing sidecar.
+			if (t.closest?.("[data-juneau-slot-table]")) return;
 			// Skip nested tables (a table inside a row-detail section).  They init lazily - after the
 			// parent detail GET succeeds and their pane is visible (see prepareNestedTable) - never on this eager
 			// page-load scan.  At DOMContentLoaded a nested table is still inert inside its <template> and is not
@@ -8493,14 +8502,14 @@
 	 * or a titled node inside ribbon/paging/toolbar chrome whose {@code title} is then moved onto
 	 * {@code data-jc-tip}.  Form fields never qualify.
 	 */
-	function cursorTipHost(t) {
+	function cursorTipHost(t) { // NOSONAR javascript:S3776 -- encodes a views/widgets state machine; complexity is inherent.
 		const start = elementFromEventTarget(t);
 		if (!start) return null;
 
 		let n = start;
 		while (n?.nodeType === 1) {
 			if (isFormFieldTag(n)) return null;
-			const explicit = typeof n.getAttribute === "function" ? n.getAttribute("data-jc-tip") : null;
+			const explicit = typeof n.getAttribute === "function" ? n.getAttribute("data-jc-tip") : null; // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 			if (explicit != null) return String(explicit).trim() === "" ? null : n;
 			n = n.parentNode;
 		}
@@ -8573,7 +8582,7 @@
 		cursorTipBound = true;
 		document.addEventListener("mouseover", function (e) {
 			const host = cursorTipHost(e.target);
-			if (host) cursorTipShow(host.getAttribute("data-jc-tip") || "", e.clientX, e.clientY);
+			if (host) cursorTipShow(host.getAttribute("data-jc-tip") || "", e.clientX, e.clientY); // NOSONAR javascript:S7761 -- reads are deliberately attribute-based; this file's duck-typed/test-shim DOM does not guarantee the .dataset API (see the notes above)
 		});
 		document.addEventListener("mousemove", function (e) {
 			const el = document.getElementById(JC_TIP_ID);
diff --git a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeCss_NavRules_Test.java b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeCss_NavRules_Test.java
index 178dbad11c..5afe4502a9 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeCss_NavRules_Test.java
+++ b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeCss_NavRules_Test.java
@@ -36,6 +36,9 @@ import org.junit.jupiter.api.*;
  */
 class ChromeCss_NavRules_Test extends TestBase {
 
+	@SuppressWarnings({
+		"java:S5852" // Possessive quantifiers: no backtracking; input is the module's own bundled stylesheet.
+	})
 	private static final Pattern RULE = Pattern.compile("([^{}]++)\\{[^}]*+\\}");
 
 	private static String read(Class<?> anchor, String path) throws IOException {
diff --git a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_SlotMount_Test.java b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_SlotMount_Test.java
index dd724d29d2..b2bd88697c 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_SlotMount_Test.java
+++ b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_SlotMount_Test.java
@@ -164,4 +164,9 @@ class ViewsJs_SlotMount_Test extends TestBase {
 		assertAllTrue(r, "t18_relativeKept", "t18_sameOriginAbsKept", "t18_noWarnYet", "t18_crossOriginNotStamped",
 			"t18_warned");
 	}
+
+	@Test void b15_documentScanLeavesASlotBuiltTableAlone() {
+		var r = report();
+		assertAllTrue(r, "t19_tableMounted", "t19_scanLogsNoMissingSidecar");
+	}
 }
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/async-job.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/async-job.cjs
index d45718a2be..fb64866e45 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/async-job.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/async-job.cjs
@@ -54,7 +54,7 @@ const PROBE = async function () {
 
 	// NOSONAR javascript:S7721 -- must stay nested inside PROBE: page.evaluate(PROBE) serializes only this
 	// function's own source, so a helper moved to module scope would not exist in the browser page it runs in.
-	function rendered(el) {
+	function rendered(el) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		if (!el) return false;
 		const r = el.getBoundingClientRect();
 		return r.width > 0 && r.height > 0;
@@ -62,7 +62,7 @@ const PROBE = async function () {
 
 	// A minimal row with an actions cell and a trigger button (setRowJobRunning disables the trigger).
 	// NOSONAR javascript:S7721 -- must stay nested inside PROBE (see `rendered` above for why).
-	function makeRow(rowId) {
+	function makeRow(rowId) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		const table = document.createElement('table');
 		table.dataset.juneauView = 'v';
 		table.dataset.juneauCsrf = 'tok-xyz';   // so the fail-closed cancel POST is armed
@@ -83,11 +83,11 @@ const PROBE = async function () {
 
 	// A fake fetch Response with a synchronous headers.get and an async text().
 	// NOSONAR javascript:S7721 -- must stay nested inside PROBE (see `rendered` above for why).
-	function resp(o) {
+	function resp(o) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return {
 			ok: o.ok,
 			status: o.status,
-			headers: { get: n => (o.headers || {})[n] || null },
+			headers: { get: n => o.headers?.[n] || null },
 			text: () => Promise.resolve(o.body != null ? o.body : '')
 		};
 	}
@@ -114,7 +114,7 @@ const PROBE = async function () {
 
 	function lastEs() { return esInstances.at(-1); }
 	// NOSONAR javascript:S7721 -- must stay nested inside PROBE (see `rendered` above for why).
-	function jobBanner(dom) { return dom.td.querySelector('.juneau-view-job-progress'); }
+	function jobBanner(dom) { return dom.td.querySelector('.juneau-view-job-progress'); } // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 	function jobMsg(dom) {
 		const b = jobBanner(dom);
 		const m = b ? b.querySelector('.juneau-view-job-progress-msg') : null;
@@ -193,7 +193,7 @@ const PROBE = async function () {
 		if (fetchCalls.length > 0) {
 			out.cancel.url = fetchCalls[0].url;
 			out.cancel.method = fetchCalls[0].opts.method;
-			out.cancel.csrfHeader = (fetchCalls[0].opts.headers || {})['X-Csrf-Token'];
+			out.cancel.csrfHeader = fetchCalls[0].opts.headers?.['X-Csrf-Token'];
 		}
 		// The authoritative terminal outcome still arrives over the stream: cancelled.
 		lastEs().emit('result', JSON.stringify({ contractVersion: V, outcome: 'cancelled' }));
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/cell-popover.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/cell-popover.cjs
index 856bc64679..7dc7925326 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/cell-popover.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/cell-popover.cjs
@@ -105,7 +105,7 @@ const VOID_TAGS = { br: 1, hr: 1, img: 1, input: 1, meta: 1, link: 1, base: 1 };
 
 function parseAttrs(raw, node) {
 	if (!raw) return;
-	const re = /([:@\w-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;
+	const re = /([:@\w-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g; // NOSONAR javascript:S5843 -- mini attribute tokenizer; alternation shape is inherent to the quoting forms it accepts
 	let m;
 	for (m = re.exec(raw); m; m = re.exec(raw))
 		node.setAttribute(m[1], m[2] ?? m[3] ?? m[4] ?? '');
@@ -174,15 +174,15 @@ const sandbox = { window: window, document: document, console: console, DOMParse
 // NOSONAR javascript:S1523 -- loading the production juneau-renders.js/juneau-views.js sources into a VM sandbox
 // is this harness's intended mechanism for exercising them under the local DOM shim; inputs are fixed local file
 // paths supplied by the test, never attacker-controlled data.
-vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 // NOSONAR javascript:S1523 -- same fixed-local-file harness mechanism as above, for juneau-views.js.
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 const NS = window.JuneauViews;
 const I = NS?.init;
 /** True when {@code n} or any descendant element carries a {@code data-juneau-ts} host stamp. */
 function hasTsHost(n) {
-	if (!n || n.nodeType !== 1) return false;
+	if (n?.nodeType !== 1) return false;
 	if (n.dataset.juneauTs) return true;
 	for (const c of n.childNodes) {
 		if (hasTsHost(c)) return true;
@@ -192,7 +192,7 @@ function hasTsHost(n) {
 
 /** True when {@code n} or a descendant, other than {@code root} itself, is a SPAN element. */
 function hasSpanBelow(n, root) {
-	if (!n || n.nodeType !== 1) return false;
+	if (n?.nodeType !== 1) return false;
 	if (n !== root && n.tagName === 'SPAN') return true;
 	for (const c of n.childNodes) {
 		if (hasSpanBelow(c, root)) return true;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/chrome-tip.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/chrome-tip.cjs
index 1a9ae3baaf..a41348c3d8 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/chrome-tip.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/chrome-tip.cjs
@@ -75,7 +75,7 @@ function leave(target, related) {
 	out.paging_tipAttr = btn.dataset.jcTip;
 	out.paging_ariaKept = btn.getAttribute('aria-label') === 'Next page';
 	out.paging_tipText = tip ? tip.textContent : null;
-	out.paging_tipVisible = !!(tip?.style.display === 'block');
+	out.paging_tipVisible = tip?.style.display === 'block';
 	out.paging_tipClass = tip ? tip.className : null;
 	out.paging_tipLeft = tip ? tip.style.left : null;
 	out.paging_tipTop = tip ? tip.style.top : null;
@@ -102,7 +102,7 @@ function leave(target, related) {
 	out.ribbon_tipAttr = btn.dataset.jcTip;
 	out.ribbon_ariaKept = btn.getAttribute('aria-label') === 'Refresh';
 	out.ribbon_tipText = tipNode() ? tipNode().textContent : null;
-	out.ribbon_tipVisible = tipNode() && tipNode().style.display === 'block';
+	out.ribbon_tipVisible = tipNode()?.style.display === 'block';
 	leave(btn, env.body);
 })();
 
@@ -145,7 +145,7 @@ function leave(target, related) {
 	out.helper_tipAttr = btn.dataset.jcTip;
 	out.helper_ariaKept = btn.getAttribute('aria-label') === 'Acknowledge';
 	out.helper_tipText = tipNode() ? tipNode().textContent : null;
-	out.helper_tipVisible = tipNode() && tipNode().style.display === 'block';
+	out.helper_tipVisible = tipNode()?.style.display === 'block';
 	leave(btn, env.body);
 })();
 
@@ -156,9 +156,9 @@ function leave(target, related) {
 
 	hover(span, 30, 40);
 	out.explicit_tipText = tipNode() ? tipNode().textContent : null;
-	out.explicit_tipVisible = tipNode() && tipNode().style.display === 'block';
+	out.explicit_tipVisible = tipNode()?.style.display === 'block';
 	leave(span, env.body);
-	out.explicit_hiddenOnLeave = tipNode() && tipNode().style.display === 'none';
+	out.explicit_hiddenOnLeave = tipNode()?.style.display === 'none';
 })();
 
 (function idempotentInit() {
@@ -174,7 +174,7 @@ function leave(target, related) {
 	env.body.appendChild(pill);
 	hover(btn, 12, 12);
 	out.reinit_stillWorks = btn.dataset.jcTip === 'Previous page'
-		&& tipNode() && tipNode().textContent === 'Previous page'
+		&& tipNode()?.textContent === 'Previous page'
 		&& tipNode().style.display === 'block';
 	leave(btn, env.body);
 })();
@@ -193,7 +193,7 @@ function leave(target, related) {
 	hover(btn, 40, 50);
 	out.menubtn_stillNoNativeTitle = btn.getAttribute('title') == null && (btn.title === '' || btn.title == null);
 	out.menubtn_tipText = tipNode() ? tipNode().textContent : null;
-	out.menubtn_tipVisible = !!(tipNode() && tipNode().style.display === 'block');
+	out.menubtn_tipVisible = !!(tipNode()?.style.display === 'block');
 	leave(btn, env.body);
 })();
 
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/client-column-search.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/client-column-search.cjs
index 204ae9904a..05e0f93e42 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/client-column-search.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/client-column-search.cjs
@@ -66,7 +66,7 @@ if (!out.hasGetColumnExpr || !out.hasSetColumnExpr || !out.hasCompile) {
 // ---------------------------------------------------------------------------------------------------------------
 
 function op(name, extra) {
-	return Object.assign({ name: name, help: name + ' help.', minArgs: 1, maxArgs: 1, combinator: false, custom: false }, extra || {});
+	return Object.assign({ name: name, help: name + ' help.', minArgs: 1, maxArgs: 1, combinator: false, custom: false }, { ...extra });
 }
 
 const ENUM_OPS = [op('$eq'), op('$ne'), op('$in', { maxArgs: -1 }), op('$not', { combinator: true })];
@@ -115,7 +115,7 @@ function fakeCol(idx, opts) {
 	const fixed = {};
 	const header = headerCell(opts.title || ('Col' + idx));
 	const api = { draw: function () { draws++; return api; } };
-	const search = function (v) {
+	const search = function (v) { // NOSONAR javascript:S3800 -- mirrors DataTables' column.search() getter/setter overload: returns the value or the chainable api
 		if (arguments.length === 0) return applied;
 		applied = v == null ? '' : String(v);
 		return api;
@@ -160,7 +160,7 @@ function fakeDt(cols) {
  */
 function makeTable(mode, colOpts) {
 	const optsColumns = COLUMNS.map(function (c) { return { data: c.data, title: c.data }; });
-	const cols = COLUMNS.map(function (c, i) { return fakeCol(i, Object.assign({ title: c.data }, (colOpts || {})[c.data])); });
+	const cols = COLUMNS.map(function (c, i) { return fakeCol(i, { title: c.data, ...colOpts?.[c.data] }); });
 	const ctx = {
 		viewDef: { id: 't-' + mode, dataMode: mode === 'client' ? 'client' : 'server', columns: COLUMNS },
 		optsColumns: optsColumns,
@@ -286,8 +286,8 @@ out.clientFilteredInline = I.isClientFiltered({ viewDef: { dataMode: 'server' },
 	out.strictBadCode = r.error ? r.error.code : null;
 	out.strictKeptRows = JSON.stringify(visible(t)) === JSON.stringify(before);
 	out.strictKeptExpr = I.getColumnExpr(t.ctx, colByName(t, 'priority'));
-	out.strictUnknownOpCode = (setExpr(t, 'priority', '$nope(1)').error || {}).code;
-	out.strictOutOfTypeCode = (setExpr(t, 'status', '$gt(1)').error || {}).code;
+	out.strictUnknownOpCode = setExpr(t, 'priority', '$nope(1)').error?.code;
+	out.strictOutOfTypeCode = setExpr(t, 'status', '$gt(1)').error?.code;
 	// Blank clears: the fixed predicate is removed and the store entry dropped.
 	const cleared = setExpr(t, 'priority', '');
 	out.strictClearOk = cleared.ok;
@@ -440,7 +440,7 @@ if (datesJsonPath) {
 		};
 		const t = { ctx: ctx, cols: cols };
 		const r = I.setColumnExpr(ctx, cols[0], '$between(2026-02-01,2026-03-31)');
-		out.dateBetween[c] = r.ok ? visible(t, rows) : ('error:' + (r.error && r.error.code));
+		out.dateBetween[c] = r.ok ? visible(t, rows) : ('error:' + r.error?.code);
 	});
 	// Popover UTC note on a timestamp column.
 	const cols = [fakeCol(0, { title: 'date' })];
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/column-search-popover.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/column-search-popover.cjs
index 310540dda5..5497b3d674 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/column-search-popover.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/column-search-popover.cjs
@@ -74,7 +74,7 @@ function fakeCol(idx, header) {
 	return {
 		index: function () { return idx; },
 		header: function () { return header; },
-		search: function (v) {
+		search: function (v) { // NOSONAR javascript:S3800 -- mirrors DataTables' column.search() getter/setter overload: returns the value or the chainable api
 			if (arguments.length === 0) return applied;
 			applied = v == null ? '' : String(v);
 			return api;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/column-search.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/column-search.cjs
index 204e9fe8c3..1f7fc36c1a 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/column-search.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/column-search.cjs
@@ -45,7 +45,7 @@ if (!searchJsPath || !corpusPath) {
 
 const context = vm.createContext({ window: {} });
 // NOSONAR javascript:S1523 -- loading a production JS source into a VM sandbox is this harness's intended mechanism; the input is a fixed local file supplied by the test.
-vm.runInContext(fs.readFileSync(searchJsPath, 'utf8'), context, { filename: 'juneau-search.js' });
+vm.runInContext(fs.readFileSync(searchJsPath, 'utf8'), context, { filename: 'juneau-search.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 const S = context.window.JuneauViews?.search;
 if (!S || typeof S.createEngine !== 'function' || typeof S.parseExprStrict !== 'function' || typeof S.resolveStrict !== 'function') {
 	console.error('juneau-search.js did not publish window.JuneauViews.search');
@@ -209,7 +209,7 @@ function resolvesCleanly(kase) {
 	if (type === null || (kase.operators?.custom))
 		return true;  // Custom operators are unknown to the builtin-only strict path.
 	try { S.resolveStrict(exprStringOf(kase.search), type); return true; }
-	catch (error) { return false; }
+	catch (error) { return false; } // NOSONAR javascript:S2486 -- a throw means the strict resolver rejected the expression; that is reported as false
 }
 
 // -----------------------------------------------------------------------------------------------------------------
@@ -225,7 +225,7 @@ function runCase(kase) {
 		if (kase.tree) {
 			let ok = false;
 			try { ok = deepEqual(treeOf(S.parseExprStrict(exprStringOf(kase.search))), kase.tree.expr); }
-			catch (error) { ok = false; }
+			catch (error) { ok = false; } // NOSONAR javascript:S2486 -- a throw means the strict parser rejected the expression; recorded as a failed comparison
 			pass = pass && ok;
 		}
 		if (kase.rows)
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/config-application.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/config-application.cjs
index 400cbe8a8a..77479383be 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/config-application.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/config-application.cjs
@@ -45,7 +45,7 @@ const source = fs.readFileSync(path.resolve(configJsPath), 'utf8');
 const window = {};
 // NOSONAR javascript:S1523 -- this is the test harness deliberately loading the real
 // juneau-config.js under test into an isolated vm sandbox; there is no untrusted input.
-vm.runInNewContext(source, { window: window, console: console }, { filename: 'juneau-config.js' });
+vm.runInNewContext(source, { window: window, console: console }, { filename: 'juneau-config.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 const NS = window.JuneauViews;
 const out = { hasConfig: !!NS?.config };
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/config-chooser.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/config-chooser.cjs
index b9fd5fb6c3..e3a5a8965b 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/config-chooser.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/config-chooser.cjs
@@ -77,7 +77,7 @@ const window = { document: document, console: console, prompt: function () { ret
 // NOSONAR javascript:S1523 -- loading the production juneau-config.js source into a VM sandbox is this harness's
 // intended mechanism for exercising it against a minimal fake window/document; the input is a fixed local file
 // path supplied by the test, never attacker-controlled data.
-vm.runInNewContext(fs.readFileSync(path.resolve(configJsPath), 'utf8'), { window: window, document: document, console: console }, { filename: 'juneau-config.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(configJsPath), 'utf8'), { window: window, document: document, console: console }, { filename: 'juneau-config.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 const NS = window.JuneauViews;
 const out = { hasConfig: !!NS?.config };
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/config-persistence.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/config-persistence.cjs
index 469c050c39..a5bb82ef68 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/config-persistence.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/config-persistence.cjs
@@ -52,7 +52,7 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
-	function makeTable(pageId, viewId, savedViewsBase) {
+	function makeTable(pageId, viewId, savedViewsBase) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		const page = document.createElement('div');
 		page.dataset.juneauPage = pageId;
 		if (savedViewsBase != null) page.dataset.juneauSavedViews = savedViewsBase;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/config-reinit.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/config-reinit.cjs
index b9fb4c2a5b..23e85ebbae 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/config-reinit.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/config-reinit.cjs
@@ -61,10 +61,10 @@ const window = { document: document, console: console, jQuery: undefined };
 const sandbox = { window: window, document: document, console: console };
 // NOSONAR javascript:S1523 -- this is the test harness deliberately loading the real
 // juneau-config.js under test into an isolated vm sandbox; there is no untrusted input.
-vm.runInNewContext(fs.readFileSync(path.resolve(configJsPath), 'utf8'), sandbox, { filename: 'juneau-config.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(configJsPath), 'utf8'), sandbox, { filename: 'juneau-config.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 // NOSONAR javascript:S1523 -- same rationale: deliberately loading the real juneau-views.js under test
 // into an isolated vm sandbox; there is no untrusted input.
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 const NS = window.JuneauViews;
 const out = {
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/console-dom-shim.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/console-dom-shim.cjs
index 47d27a6c5a..126ef6f057 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/console-dom-shim.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/console-dom-shim.cjs
@@ -38,7 +38,7 @@ const shim = require('./views-dom-shim.cjs');
  * DocumentFragment (nodeType 11) - importing it must yield a real fragment (its children flattened on append),
  * never a wrapper element literally named `template-content`.
  */
-function cloneDeep(doc, node, deep) {
+function cloneDeep(doc, node, deep) { // NOSONAR javascript:S3776 -- shim DOM clone mirrors node-type cases; complexity is inherent
 	if (node.nodeType === 3) return doc.createTextNode(node.textContent);
 	if (node.nodeType === 11 || node.tagName === 'TEMPLATE-CONTENT') {
 		const frag = doc.createDocumentFragment();
@@ -80,7 +80,7 @@ function decodeEntities(s) {
 function parseHtmlFragment(doc, html) {
 	const frag = makeFragment();
 	const stack = [frag];
-	const tagRe = /<\/?[a-zA-Z][a-zA-Z0-9-]*[^>]*>/g;
+	const tagRe = /<\/?[a-zA-Z][^>]*>/g;
 	let last = 0, m;
 	function appendText(text) {
 		const decoded = decodeEntities(text);
@@ -220,7 +220,8 @@ function makeConsoleEnv(opts) {
 	const listenerCounts = {};
 	const origAddEventListener = doc.addEventListener.bind(doc);
 	doc.addEventListener = function (type, fn) {
-		(listenerCounts[type] = listenerCounts[type] || []).push(fn);
+		listenerCounts[type] = listenerCounts[type] || [];
+		listenerCounts[type].push(fn);
 		origAddEventListener(type, fn);
 	};
 	doc.dispatchEvent = function (ev) {
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/datatables-ajax.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/datatables-ajax.cjs
index 97d87b4601..337c62720d 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/datatables-ajax.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/datatables-ajax.cjs
@@ -78,21 +78,21 @@ const PROBE = async function () {
 		return true;
 	}
 
-	function infoText(id) {
+	function infoText(id) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		const el = document.querySelector('#' + id + '_wrapper .dt-info, #' + id + '_wrapper .dataTables_info');
 		return el ? el.textContent : null;
 	}
-	function firstRowText(id, col) {
+	function firstRowText(id, col) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		const row = document.querySelector('#' + id + ' tbody tr');
 		return row?.children[col] ? row.children[col].textContent : null;
 	}
-	function searchInput(id) {
+	function searchInput(id) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return document.querySelector('#' + id + '_wrapper input[type=search]');
 	}
-	function nextButton(id) {
+	function nextButton(id) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return document.querySelector('#' + id + '_wrapper .dt-paging-button.next, #' + id + '_wrapper .paginate_button.next');
 	}
-	function nameHeader(id) {
+	function nameHeader(id) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return document.querySelector('#' + id + ' thead th:nth-child(2)');
 	}
 	function isFiltered(id) {
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot-browser.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot-browser.cjs
index ee9971175d..af8e4f05f4 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot-browser.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot-browser.cjs
@@ -70,7 +70,7 @@ const PROBE = async function () {
 
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page.
-	function overlapsAnyTab(r) {
+	function overlapsAnyTab(r) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return r.tabs.some(function (t) {
 			return r.slot.left < t.right && t.left < r.slot.right && r.slot.top < t.bottom && t.top < r.slot.bottom;
 		});
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot.cjs
index b534128d7a..4ca0f9428b 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/detail-bar-slot.cjs
@@ -62,8 +62,8 @@ function matchesAttrToken(node, name, value) {
 	return value === undefined || v === value;
 }
 
-function elMatches(node, sel) {
-	if (!node || node.nodeType !== 1) return false;
+function elMatches(node, sel) { // NOSONAR javascript:S3776 -- linear test scenario; complexity is inherent
+	if (node?.nodeType !== 1) return false;
 	if (sel.indexOf(',') >= 0)
 		return sel.split(',').some(function (part) { return elMatches(node, part.trim()); });
 	let rest = sel.trim();
@@ -318,11 +318,11 @@ const sandbox = {
 // NOSONAR javascript:S1523 -- this harness's entire purpose is to load the production runtime under test (a
 // repo-local file path from argv, not attacker-controlled input) into an isolated VM sandbox; that IS the test.
 if (rendersJsPath)
-	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' });
+	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 // NOSONAR javascript:S1523 -- same rationale: loading the production juneau-views.js under test into the sandbox.
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 // NOSONAR javascript:S1523 -- same rationale: loading the production juneau-console.js under test into the sandbox.
-vm.runInNewContext(fs.readFileSync(path.resolve(consoleJsPath), 'utf8'), sandbox, { filename: 'juneau-console.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(consoleJsPath), 'utf8'), sandbox, { filename: 'juneau-console.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 const VNS = window.JuneauViews;
 const V = VNS?.init;
@@ -331,11 +331,11 @@ const C = CNS?.chrome;
 
 const out = {
 	hasViews: !!(typeof V?.buildRibbonStrip === 'function' && typeof V?.relocateDetailBarSlot === 'function'),
-	hasChrome: !!(typeof C?.initAll === 'function'),
-	hasRelocate: !!(typeof V?.relocateDetailBarSlot === 'function'),
-	hasMint: !!(typeof V?.mintDetailBarSlotIdentity === 'function'),
-	hasTeardown: !!(typeof V?.teardownDetailBarSlot === 'function'),
-	hasEnhance: !!(typeof V?.enhanceChromeInPanel === 'function')
+	hasChrome: typeof C?.initAll === 'function',
+	hasRelocate: typeof V?.relocateDetailBarSlot === 'function',
+	hasMint: typeof V?.mintDetailBarSlotIdentity === 'function',
+	hasTeardown: typeof V?.teardownDetailBarSlot === 'function',
+	hasEnhance: typeof V?.enhanceChromeInPanel === 'function'
 };
 if (!(out.hasViews && out.hasChrome && out.hasRelocate && out.hasMint && out.hasTeardown && out.hasEnhance)) {
 	process.stdout.write(JSON.stringify(out));
@@ -582,8 +582,8 @@ let stripB = null;
 	// Each minted id round-trips through the shipped readSidecar (prefix + marker), and the AUTHOR id no longer does.
 	const rtA = C.readSidecar(SIDECAR_PREFIX, out.mint_markerA);
 	const rtB = C.readSidecar(SIDECAR_PREFIX, out.mint_markerB);
-	out.mint_roundTripA = !!(rtA?.badges?.['bar:open'] === 3);
-	out.mint_roundTripB = !!(rtB?.badges?.['bar:open'] === 7);
+	out.mint_roundTripA = rtA?.badges?.['bar:open'] === 3;
+	out.mint_roundTripB = rtB?.badges?.['bar:open'] === 7;
 	out.mint_authorIdUnresolvable = document.getElementById(SIDECAR_PREFIX + AUTHOR_ID) === null;
 	out.mint_authorSidecarUnresolvable = C.readSidecar(SIDECAR_PREFIX, AUTHOR_ID) === null;
 })();
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/detail-field-grid-browser.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/detail-field-grid-browser.cjs
index d499362879..5341802659 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/detail-field-grid-browser.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/detail-field-grid-browser.cjs
@@ -38,7 +38,7 @@ const { chromium } = require('playwright');
 
 /** Fills the client-painted slots the way the runtime does, so the grid measures against real content. */
 const SETUP = function (longValue) {
-	const q = function (sel) { return document.querySelector(sel); };
+	const q = function (sel) { return document.querySelector(sel); }; // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 	q('[data-juneau-field="name"]').textContent = 'alerts-primary';
 	q('[data-juneau-field="owner"]').textContent = 'Platform';
 	q('[data-juneau-field="region"]').textContent = 'us-east-1';
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/detail-region-row-id.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/detail-region-row-id.cjs
index 7859deca3d..5eba59661c 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/detail-region-row-id.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/detail-region-row-id.cjs
@@ -193,7 +193,7 @@ async function run(opts) {
 	// The property every consumer actually depends on, asserted the way they resolve it.
 	const region = ok.fx.panel.querySelector('[data-juneau-region]');
 	out.regionResolvesRowIdByClosest =
-		region.closest('[data-juneau-row-id]')?.getAttribute('data-juneau-row-id') === ROW_ID;
+		region.closest('[data-juneau-row-id]')?.getAttribute('data-juneau-row-id') === ROW_ID; // NOSONAR javascript:S7761 -- deliberately asserts the literal data-juneau-row-id attribute name consumers resolve
 
 	// End to end: the region's declared `{id}` url was substituted with the REAL id.
 	out.regionFetchedSubstitutedUrl = ok.fetches.indexOf('/scripts/' + ROW_ID + '/source') >= 0;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs
index f914f86b5f..7bd028218c 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs
@@ -65,8 +65,8 @@ function matchesAttrToken(node, name, value) {
 	return value === undefined || v === value;
 }
 
-function elMatches(node, sel) {
-	if (!node || node.nodeType !== 1) return false;
+function elMatches(node, sel) { // NOSONAR javascript:S3776 -- linear test scenario; complexity is inherent
+	if (node?.nodeType !== 1) return false;
 	if (sel.indexOf(',') >= 0)
 		return sel.split(',').some(function (part) { return elMatches(node, part.trim()); });
 	let rest = sel.trim();
@@ -301,10 +301,10 @@ const sandbox = {
 // NOSONAR javascript:S1523 -- this harness's entire purpose is to load the production runtime under test (a
 // repo-local file path from argv, not attacker-controlled input) into an isolated VM sandbox; that IS the test.
 if (rendersJsPath)
-	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' });
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 // NOSONAR javascript:S1523 -- same rationale: loading the production juneau-console.js under test into the sandbox.
-vm.runInNewContext(fs.readFileSync(path.resolve(consoleJsPath), 'utf8'), sandbox, { filename: 'juneau-console.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(consoleJsPath), 'utf8'), sandbox, { filename: 'juneau-console.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 const VNS = window.JuneauViews;
 const V = VNS?.init;
@@ -312,13 +312,13 @@ const CNS = window.JuneauConsole;
 const C = CNS?.chrome;
 
 const out = {
-	hasViews: !!(typeof V?.buildDialogOverlay === 'function'),
-	hasChrome: !!(typeof C?.initAll === 'function'),
-	hasBuildRegion: !!(typeof V?.buildDialogBarSlotRegion === 'function'),
-	hasInsert: !!(typeof V?.insertDialogBarSlot === 'function'),
-	hasMint: !!(typeof V?.mintDetailBarSlotIdentity === 'function'),
-	hasTeardown: !!(typeof V?.teardownDetailBarSlot === 'function'),
-	hasEnhance: !!(typeof V?.enhanceChromeInPanel === 'function')
+	hasViews: typeof V?.buildDialogOverlay === 'function',
+	hasChrome: typeof C?.initAll === 'function',
+	hasBuildRegion: typeof V?.buildDialogBarSlotRegion === 'function',
+	hasInsert: typeof V?.insertDialogBarSlot === 'function',
+	hasMint: typeof V?.mintDetailBarSlotIdentity === 'function',
+	hasTeardown: typeof V?.teardownDetailBarSlot === 'function',
+	hasEnhance: typeof V?.enhanceChromeInPanel === 'function'
 };
 if (!(out.hasViews && out.hasChrome && out.hasBuildRegion && out.hasInsert && out.hasMint && out.hasTeardown
 		&& out.hasEnhance)) {
@@ -399,7 +399,7 @@ function regionsIn(root) {
 		try {
 			const j = JSON.parse(sidecar.textContent);
 			return j.contractVersion === '1' && j.badges['bar:open'] === 3;
-		} catch (error) { return false; }
+		} catch (error) { return false; } // NOSONAR javascript:S2486 -- unparseable sidecar JSON is the failure being probed; reported as false
 	})();
 })();
 
@@ -434,8 +434,8 @@ const fB = dialogFixture();
 
 	const rtA = C.readSidecar(SIDECAR_PREFIX, out.mint_markerA);
 	const rtB = C.readSidecar(SIDECAR_PREFIX, out.mint_markerB);
-	out.mint_roundTripA = !!(rtA?.badges?.['bar:open'] === 3);
-	out.mint_roundTripB = !!(rtB?.badges?.['bar:open'] === 7);
+	out.mint_roundTripA = rtA?.badges?.['bar:open'] === 3;
+	out.mint_roundTripB = rtB?.badges?.['bar:open'] === 7;
 })();
 
 // ------------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-form-browser.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-form-browser.cjs
index 670ea534ce..3381c56efd 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-form-browser.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-form-browser.cjs
@@ -44,13 +44,13 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
-	function rendered(el) { if (!el) { return false; } const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; }
+	function rendered(el) { if (!el) { return false; } const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; } // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 	function drain() { while (init.topLayer()) init.popLayer(); }
 
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
-	function makeRow(rowId) {
+	function makeRow(rowId) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		const table = document.createElement('table');
 		const tbody = document.createElement('tbody');
 		const tr = document.createElement('tr');
@@ -71,7 +71,7 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
-	function sixTypeForm() {
+	function sixTypeForm() { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return {
 			title: 'Acknowledge?',
 			idempotencyKey: 'key-ack',
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-receipt.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-receipt.cjs
index 65f24db3a6..6826177f09 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-receipt.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-receipt.cjs
@@ -169,7 +169,7 @@ async function openAndConfirm(opts) {
 
 const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'success', ...extra }));
 
-(async function main() {
+(async function main() { // NOSONAR javascript:S3776 -- linear test scenario; complexity is inherent
 
 	// --- 1) T8-receipt: the swap.  Same layer, no submit controls, the submitted form torn down ---------------
 	{
@@ -199,8 +199,7 @@ const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'succes
 			return dl?.querySelectorAll('dd').length === 2 && dl.querySelectorAll('dt').length === 2;
 		})();
 		out.receipt_exactlyOneFieldList = env.body.querySelectorAll('.juneau-view-dialog-fields').length === 1;
-		out.receipt_codeFieldIsPre = q(env, '.juneau-view-dialog-field-code') != null
-			&& q(env, '.juneau-view-dialog-field-code').textContent === 'tok-9';
+		out.receipt_codeFieldIsPre = q(env, '.juneau-view-dialog-field-code')?.textContent === 'tok-9';
 		out.receipt_codeFieldHasCopyButton = q(env, '[data-testid="dialog-field-copy"]') != null;
 		// The row banner is SUPPRESSED: the dialog is on screen, so rendering it too would show one outcome twice.
 		out.receipt_noRowBanner = !anyRowBanner(env);
@@ -233,7 +232,7 @@ const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'succes
 		const env = r.env;
 		out.f4_dialogClosedAsToday = r.I.dialogLayerCount() === 0;
 		out.f4_noFollowUpGet = receiptGets === 0;
-		out.f4_successBannerUnchanged = rowOutcome(env) != null && rowOutcome(env).dataset.state === 'success';
+		out.f4_successBannerUnchanged = rowOutcome(env)?.dataset.state === 'success';
 		const diag = q(env, '[data-testid="result-form-ignored"]');
 		out.f4_diagnosticPresent = diag != null;
 		// role=status, not alert: the write SUCCEEDED, and a consumer authoring bug must not look like a failure.
@@ -315,7 +314,7 @@ const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'succes
 		out.t1_stillOpen = r.I.dialogLayerCount() === 1;
 		out.t1_noticePresent = heldNotice(env) != null;
 		// Retryable: nothing was sent, so the operator can fix the declaration and press Confirm again.
-		out.t1_confirmReEnabled = confirmBtn(env) != null && confirmBtn(env).disabled === false;
+		out.t1_confirmReEnabled = confirmBtn(env)?.disabled === false;
 		out.t1_busyCleared = dialogEl(env).dataset.juneauDialogBusy === undefined;
 		out.t1_noCloseOnlyRow = q(env, '[data-testid="dialog-result-close"]') === null;
 		out.t1_noRowBanner = !anyRowBanner(env);
@@ -350,7 +349,7 @@ const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'succes
 		const env = r.env;
 		out.t4_stillOpen = r.I.dialogLayerCount() === 1;
 		out.t4_noticePresent = heldNotice(env) != null;
-		out.t4_confirmReEnabled = confirmBtn(env) != null && confirmBtn(env).disabled === false;
+		out.t4_confirmReEnabled = confirmBtn(env)?.disabled === false;
 		out.t4_noCloseOnlyRow = q(env, '[data-testid="dialog-result-close"]') === null;
 		out.t4_busyCleared = dialogEl(env).dataset.juneauDialogBusy === undefined;
 	}
@@ -374,7 +373,7 @@ const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'succes
 			submit: () => Promise.resolve(jsonResponse({ outcome: 'refusal', refusalCode: 'write-guard:not-armed' }))
 		});
 		const env = r.env;
-		out.t8refuse_confirmReEnabled = confirmBtn(env) != null && confirmBtn(env).disabled === false;
+		out.t8refuse_confirmReEnabled = confirmBtn(env)?.disabled === false;
 		out.t8refuse_noCloseOnlyRow = q(env, '[data-testid="dialog-result-close"]') === null;
 		out.t8refuse_noticePresent = heldNotice(env) != null;
 	}
@@ -409,7 +408,7 @@ const success = (extra) => () => Promise.resolve(jsonResponse({ outcome: 'succes
 		// no-SSE-transport fallback - which is itself a ROW paint, and therefore proves the same ordering: the
 		// close already happened, and the row was reachable when the job path ran.
 		out.t5_rowAnchoredSignalVisible = rowOutcome(env) != null;
-		out.t5_rowSignalIsNotInADialog = rowOutcome(env) != null && rowOutcome(env).closest('.juneau-view-dialog') === null;
+		out.t5_rowSignalIsNotInADialog = rowOutcome(env)?.closest('.juneau-view-dialog') === null;
 	}
 
 	// --- 12) Cancel mid-flight: the layer is gone, so the outcome falls back to the row banner -----------
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/helpers-harness.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/helpers-harness.cjs
index 5bc59eae2c..cf0af802ff 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/helpers-harness.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/helpers-harness.cjs
@@ -56,7 +56,7 @@ function load(rendersJsPath, viewsJsPath, helpersJsPath) {
 		// NOSONAR javascript:S1523 -- loading the production juneau-renders.js/juneau-views.js/juneau-helpers.js
 		// sources into a VM sandbox is this harness's intended mechanism for exercising them under the DOM shim;
 		// inputs are fixed local file paths supplied by the test, never attacker-controlled data.
-		vm.runInNewContext(fs.readFileSync(path.resolve(file), 'utf8'), sandbox, { filename: path.basename(file) });
+		vm.runInNewContext(fs.readFileSync(path.resolve(file), 'utf8'), sandbox, { filename: path.basename(file) }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 	}
 
 	const NS = env.window.JuneauViews;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/helpers.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/helpers.cjs
index 78a7ebeca8..8ea9b0ddb0 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/helpers.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/helpers.cjs
@@ -239,7 +239,7 @@ await (async function dataPaneTests() {
 	const pane3 = env.el('div');
 	const populate3 = H.dataPane({ load: function () { return Promise.reject(new Error('kaboom')); }, render: function () { return env.el('span'); } });
 	let threw3 = false;
-	try { await populate3(pane3, {}); } catch (error) { threw3 = true; }
+	try { await populate3(pane3, {}); } catch (error) { threw3 = true; } // NOSONAR javascript:S2486 -- the thrown flag is the recorded result: the test asserts nothing is rethrown
 	out.dataPane_otherRejectionNeverRethrows = threw3 === false;
 	out.dataPane_otherRejectionPaintsErrorStatus = pane3.querySelectorAll('[role="status"]').length === 1;
 
@@ -247,7 +247,7 @@ await (async function dataPaneTests() {
 	const pane3b = env.el('div');
 	const populate3b = H.dataPane({ load: function () { throw new Error('sync throw'); }, render: function () { return env.el('span'); } });
 	let threw3b = false;
-	try { await populate3b(pane3b, {}); } catch (error) { threw3b = true; }
+	try { await populate3b(pane3b, {}); } catch (error) { threw3b = true; } // NOSONAR javascript:S2486 -- the thrown flag is the recorded result: the test asserts nothing is rethrown
 	out.dataPane_syncThrowNeverRethrows = threw3b === false;
 
 	// empty result (array of length 0) -> the empty node, custom empty() honored
@@ -299,7 +299,7 @@ function keyOnTab(wrapper, focusedBtn, key) {
 	stripOf(wrapper).dispatch('keydown', { key: key, target: focusedBtn, preventDefault: function () { /* no-op */ } });
 }
 function selectedTabId(wrapper) {
-	const sel = wrapper.querySelectorAll('[role="tab"]').filter(b => b.getAttribute('aria-selected') === 'true')[0];
+	const sel = wrapper.querySelectorAll('[role="tab"]').find(b => b.getAttribute('aria-selected') === 'true');
 	return sel ? sel.dataset.juneauStripTab : null;
 }
 
@@ -403,7 +403,7 @@ await (async function tabStripTests() {
 	out.tabStrip_arrowRight_skipsDisabled = selectedTabId(wrap10) === 't4';
 	// unhandled key changes nothing
 	const beforeKey = selectedTabId(wrap10);
-	keyOnTab(wrap10, btns.filter(b => b.getAttribute('aria-selected') === 'true')[0], 'x');
+	keyOnTab(wrap10, btns.find(b => b.getAttribute('aria-selected') === 'true'), 'x');
 	out.tabStrip_unhandledKeyChangesNothing = selectedTabId(wrap10) === beforeKey;
 
 	// two independent instances: fill-once state does not leak between them (purity, positive half of test 29)
@@ -483,7 +483,7 @@ function clickPencil(leaf) {
 	btn.dispatch('click', { target: btn });
 }
 
-await (async function editableFieldTests() {
+await (async function editableFieldTests() { // NOSONAR javascript:S3776 -- linear test scenario; complexity is inherent
 	out.editableField_missingOnSaveThrows = !!throws(function () { H.editableField({ label: 'T', value: 'x' }); });
 	out.editableField_unknownTypeThrows = !!throws(function () { H.editableField({ type: 'date', onSave: function () { /* no-op */ } }); });
 	out.editableField_selectWithoutOptionsThrows = !!throws(function () {
@@ -514,7 +514,7 @@ await (async function editableFieldTests() {
 
 	const cb = H.editableField({ label: 'On', type: 'checkbox', value: false, onSave: function () { return Promise.resolve(); } });
 	out.editableField_checkboxHasNoPencil = !cb.querySelector('.jc-editable-field-pencil');
-	out.editableField_checkboxIsInput = cb.querySelector('input') && cb.querySelector('input').type === 'checkbox';
+	out.editableField_checkboxIsInput = cb.querySelector('input')?.type === 'checkbox';
 
 	let disabledSaved = 0;
 	const disabledLeaf = H.editableField({
@@ -592,7 +592,7 @@ await (async function editableFieldTests() {
 	await flush();
 	out.editableField_explicitBlurDoesNotSave = explicitSaves.length === 0 && cls(explicitLeaf, 'is-editing');
 	const explicitBtns = explicitLeaf.querySelectorAll('button');
-	const saveBtn = explicitBtns.filter(b => textOf(b).indexOf('Save') >= 0)[0];
+	const saveBtn = explicitBtns.find(b => textOf(b).indexOf('Save') >= 0);
 	saveBtn.dispatch('click', {});
 	await flush();
 	out.editableField_explicitSaveButtonSaves = explicitSaves.length === 1 && explicitSaves[0] === 'typed';
@@ -604,7 +604,7 @@ await (async function editableFieldTests() {
 	});
 	clickPencil(cancelLeaf);
 	cancelLeaf.querySelector('input').value = 'nope';
-	cancelLeaf.querySelectorAll('button').filter(b => textOf(b).indexOf('Cancel') >= 0)[0].dispatch('click', {});
+	cancelLeaf.querySelectorAll('button').find(b => textOf(b).indexOf('Cancel') >= 0).dispatch('click', {});
 	out.editableField_explicitCancelDiscards = cancelSaves === 0 && !cls(cancelLeaf, 'is-editing');
 
 	let selectSaved = [];
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/icons.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/icons.cjs
index 4e6814ea6b..c7e3bb4277 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/icons.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/icons.cjs
@@ -161,8 +161,9 @@ async function runScenario(opts) {
 	const sandbox = {
 		window: env.window, document: env.document, console: sandboxConsole,
 		setTimeout: function (fn) {
-			if (typeof fn !== 'function') return 0;
-			if (opts.deferTimers) timers.push(fn); else fn();
+			if (typeof fn === 'function') {
+				if (opts.deferTimers) timers.push(fn); else fn();
+			}
 			return 0;
 		},
 		clearTimeout: function () { /* no-op */ },
@@ -172,7 +173,7 @@ async function runScenario(opts) {
 	};
 	// NOSONAR javascript:S1523 -- loading the production juneau-icons.js source into a VM sandbox is this
 	// harness's intended mechanism for exercising it under the SVG-sprite shim; the path is a fixed local file.
-	vm.runInNewContext(ICONS_JS, sandbox, { filename: 'juneau-icons.js' });
+	vm.runInNewContext(ICONS_JS, sandbox, { filename: 'juneau-icons.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 
 	const NS = env.window.JuneauViews;
 	const ctx = {
@@ -367,7 +368,7 @@ const OVR_SVG =
 		const s = await runScenario({ replacementUrl: '/app/repl.svg', replacementSvg: REPL_SVG });
 		const stems = s.NS.icons.stems().slice();
 		out.s_catalog = {
-			stemsMatchShipped: JSON.stringify(stems.sort((a, b) => Number(a > b) - Number(a < b))) === JSON.stringify(shipped.sort((a, b) => Number(a > b) - Number(a < b))),
+			stemsMatchShipped: JSON.stringify(stems.toSorted((a, b) => Number(a > b) - Number(a < b))) === JSON.stringify(shipped.toSorted((a, b) => Number(a > b) - Number(a < b))),
 			brandnewInStems: stems.indexOf('brandnew') !== -1,
 			brandnewResolves: typeof s.NS.icons.resolveIcon('brandnew') === 'string'
 		};
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/juneau-console.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/juneau-console.cjs
index 7b4e40800a..fd5bba1281 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/juneau-console.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/juneau-console.cjs
@@ -116,7 +116,7 @@ function run(contract, templates, opts) {
 
 	try {
 		// NOSONAR javascript:S1523 -- loading a production JS source into a VM sandbox is this harness's intended mechanism; the input is a fixed local file supplied by the test.
-		vm.runInNewContext(shellSrc, sandbox, { filename: 'juneau-console.js' });
+		vm.runInNewContext(shellSrc, sandbox, { filename: 'juneau-console.js' }); // NOSONAR javascript:S1523 -- harness evaluates the module's own bundled script, a fixed local file
 	} catch (error) {
 		threw = { name: error.name, code: error.code, message: error.message };
 	}
@@ -159,10 +159,10 @@ const out = {};
 	out.golden.rows = r.doc.querySelectorAll('.juneau-page-nav-children').length;
 	out.golden.mainText = txt(r.doc, 'main.jc-main');
 	out.golden.templatesLeft = r.doc.querySelectorAll('template').length;
-	out.golden.cardType = r.JC.card('jc-seg-1') && r.JC.card('jc-seg-1').type;
+	out.golden.cardType = r.JC.card('jc-seg-1')?.type;
 	out.golden.activeNavSource = r.result?.activeNavSource;
 	out.golden.activeNav = r.result?.activeNav;
-	out.golden.contractHeaderTitle = r.JC.contract() && r.JC.contract().header.title;
+	out.golden.contractHeaderTitle = r.JC.contract()?.header.title;
 })();
 
 //----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/modal-result.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/modal-result.cjs
index 3591d10c94..1f152dde54 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/modal-result.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/modal-result.cjs
@@ -52,7 +52,7 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
-	function rendered(el) {
+	function rendered(el) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		if (!el) return false;
 		const r = el.getBoundingClientRect();
 		return r.width > 0 && r.height > 0;
@@ -62,7 +62,7 @@ const PROBE = async function () {
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
 	// A minimal row with an actions cell and a trigger button (setRowInFlight disables the trigger).
-	function makeRow(rowId) {
+	function makeRow(rowId) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		const table = document.createElement('table');
 		table.dataset.juneauView = 'v';
 		const tbody = document.createElement('tbody');
@@ -84,11 +84,11 @@ const PROBE = async function () {
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
 	// A fake fetch Response with a synchronous headers.get and an async text().
-	function resp(o) {
+	function resp(o) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only the probe's own source into the browser context
 		return {
 			ok: o.ok,
 			status: o.status,
-			headers: { get: n => (o.headers || {})[n] || null },
+			headers: { get: n => o.headers?.[n] || null },
 			text: () => Promise.resolve(o.body != null ? o.body : '')
 		};
 	}
@@ -178,7 +178,7 @@ const PROBE = async function () {
 		const dds = fieldsEl ? Array.from(fieldsEl.querySelectorAll('dd')) : [];
 		out.modal = {
 			backdropVisible: rendered(backdrop),
-			title: backdrop ? (backdrop.querySelector('.juneau-view-dialog-title') || {}).textContent : null,
+			title: backdrop ? backdrop.querySelector('.juneau-view-dialog-title')?.textContent : null,
 			fieldCount: dds.length,
 			evilFieldText: dds.length > 1 ? dds[1].textContent : null,
 			// The XSS proof: the HTML-shaped value must NOT have become an <img> element anywhere in the dialog.
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/nested-popup-browser.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/nested-popup-browser.cjs
index 80b9f66f98..4f611756d6 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/nested-popup-browser.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/nested-popup-browser.cjs
@@ -46,7 +46,7 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page and
 	// break every caller below.
-	function makeRow(rowId) {
+	function makeRow(rowId) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		const table = document.createElement('table');
 		const tbody = document.createElement('tbody');
 		const tr = document.createElement('tr');
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/nested-table.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/nested-table.cjs
index 5f7c2bca54..3ca591d08a 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/nested-table.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/nested-table.cjs
@@ -56,7 +56,7 @@ if (!viewsJsPath || !dataTablesJsPath || !ribbonJsPath) {
 // matches one token kind at a time (class / attr / tag); splitting the token kinds into further helpers would
 // scatter the matcher's early-return short-circuiting across files without reducing real complexity.
 function elMatchesCompound(node, sel) {
-	if (!node || node.nodeType !== 1) return false;
+	if (node?.nodeType !== 1) return false;
 	const re = /\.[\w-]+|\[[\w:-]+(?:="[^"]*")?\]|^[a-zA-Z][\w-]*/g;
 	let m;
 	let matchedSomething = false;
@@ -88,7 +88,7 @@ function elMatches(node, sel) {
 	let n = node.parentNode;
 	for (let i = parts.length - 2; i >= 0; i--) {
 		while (n?.nodeType === 1 && !elMatchesCompound(n, parts[i])) n = n.parentNode;
-		if (!n || n.nodeType !== 1) return false;
+		if (n?.nodeType !== 1) return false;
 		n = n.parentNode;
 	}
 	return true;
@@ -201,9 +201,9 @@ const sandbox = {
 };
 // NOSONAR javascript:S1523 -- this is the test harness deliberately loading the real juneau-datatables.js,
 // juneau-ribbon.js and juneau-views.js under test into an isolated vm sandbox; there is no untrusted input.
-vm.runInNewContext(fs.readFileSync(path.resolve(dataTablesJsPath), 'utf8'), sandbox, { filename: 'juneau-datatables.js' });
-vm.runInNewContext(fs.readFileSync(path.resolve(ribbonJsPath), 'utf8'), sandbox, { filename: 'juneau-ribbon.js' });
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(dataTablesJsPath), 'utf8'), sandbox, { filename: 'juneau-datatables.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
+vm.runInNewContext(fs.readFileSync(path.resolve(ribbonJsPath), 'utf8'), sandbox, { filename: 'juneau-ribbon.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 
 const NS = window.JuneauViews;
 const I = NS?.init;
@@ -451,8 +451,7 @@ function nestedWrapper(o) {
 }
 
 const fnsW = nestedWrapper({ viewId: 'events' });
-out.find_byId = I.findNestedSidecar(fnsW.wrap, 'events') != null
-	&& I.findNestedSidecar(fnsW.wrap, 'events').dataset.juneauNestedMeta === 'events';
+out.find_byId = I.findNestedSidecar(fnsW.wrap, 'events')?.dataset.juneauNestedMeta === 'events';
 // id skew -> falls back to first meta node (present, not silent no-init).
 const skewW = nestedWrapper({ viewId: 'events', metaId: 'other' });
 out.find_fallbackFirst = I.findNestedSidecar(skewW.wrap, 'events') != null;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/page-state.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/page-state.cjs
index 2f7e6fa036..a35cc2cb9c 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/page-state.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/page-state.cjs
@@ -80,7 +80,7 @@ let threw = false;
 try {
 	ps.table('blocked').set('x', 1);
 	out.blockedGet = ps.table('blocked').get('x');   // expect null - blocked read swallowed
-} catch (error) {
+} catch (error) { // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 	threw = true;
 }
 out.blockedNoThrow = !threw;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/probe-group.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/probe-group.cjs
index cfd5abf435..057b477953 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/probe-group.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/probe-group.cjs
@@ -219,7 +219,7 @@ const ctlE = I.enhanceProbeGroup(gE);
 out.empty_noSelected = ctlE.getSelected() === null;
 let emptyThrew = false;
 try { gE.dispatch('keydown', { key: 'ArrowRight', preventDefault: function () { /* no-op */ } }); }
-catch (error) { emptyThrew = true; }
+catch (error) { emptyThrew = true; } // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 out.empty_keydownNoThrow = !emptyThrew;
 
 const t1 = probe('d1', 'success', { disabled: true });
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/regions-barrier.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/regions-barrier.cjs
index 7f4de3387d..e405cc0cfb 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/regions-barrier.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/regions-barrier.cjs
@@ -51,7 +51,8 @@ function scene() {
 	const h = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
 	h.log = {};
 	h.note = function (id, msg) {
-		(h.log[id] = h.log[id] || []).push({ kind: msg.kind, at: h.clock.now() });
+		h.log[id] = h.log[id] || [];
+		h.log[id].push({ kind: msg.kind, at: h.clock.now() });
 	};
 	h.kinds = function (id) { return (h.log[id] || []).map(function (r) { return r.kind; }); };
 	h.at = function (id) { return (h.log[id] || []).map(function (r) { return r.at; }); };
@@ -100,7 +101,7 @@ function emitOnce(h, id, msg) {
 	return el;
 }
 
-(async function () {
+(async function () { // NOSONAR javascript:S3776 -- linear test scenario
 
 	// =================================================================================================================
 	// Test 49(a) - a control region that emits during its own reason:"initial" populate and initializes FIRST still
@@ -230,7 +231,7 @@ function emitOnce(h, id, msg) {
 		const h = scene();
 		out.t49d_value = h.R.BARRIER_DEADLINE_MS;
 		// Not settable through the published namespace...
-		try { h.R.BARRIER_DEADLINE_MS = 5; } catch (error) { /* strict-mode refusal is also an acceptable answer */ }
+		try { h.R.BARRIER_DEADLINE_MS = 5; } catch (error) { /* strict-mode refusal is also an acceptable answer */ } // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 		// ...nor through a global the runtime might have consulted...
 		h.env.window.JUNEAU_BARRIER_DEADLINE_MS = 5;
 		h.env.window.juneauBarrierDeadlineMs = 5;
@@ -503,7 +504,7 @@ function emitOnce(h, id, msg) {
 			if (arm === 'released') {
 				// (iv) The bounds hold under appending: a late-ENROLLED region and a region enrolled at boot while
 				// HIDDEN both still receive nothing, and the buffer is discarded once the last window closes.
-				const lateEnrolled = subscriber(h, 'lateEnrolled');
+				subscriber(h, 'lateEnrolled');
 				const hiddenAtBoot = subscriber(h, 'hiddenAtBoot', { hiddenPanel: true });
 				H.activatePanel(hiddenAtBoot);
 				h.R.activateRegion(hiddenAtBoot._juneauRegion);
@@ -675,7 +676,7 @@ function emitOnce(h, id, msg) {
 			// B subscribes synchronously during its own populate and settles BEFORE the live emit, so B is a live
 			// subscriber whose OWN window has already closed - leaving C as the LAST open window.  Its handler is
 			// what makes C's close condition become true partway through the fan-out.
-			const mkB = function () {
+			const mkB = function () { // NOSONAR javascript:S7721 -- closes over this scenario's h, cCtx, cSync and bFetch
 				subscriber(h, 'B', {
 					subscribeNow: false,
 					captureCtx: function (ctx) {
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/regions-bus.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/regions-bus.cjs
index 33f1d0ca95..9a1915a545 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/regions-bus.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/regions-bus.cjs
@@ -106,7 +106,7 @@ function kinds(list) {
 		out.t18_keys = [p.ctxs.a.key, p.ctxs.b.key, p.ctxs.c.key];
 
 		let threw = false;
-		try { p.ctxs.a.emit({ kind: 'nowhere' }, { to: 'does-not-exist' }); } catch (error) { threw = true; }
+		try { p.ctxs.a.emit({ kind: 'nowhere' }, { to: 'does-not-exist' }); } catch (error) { threw = true; } // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 		out.t19_noThrow = threw === false;
 		out.t19_warned = p.rec.warnsMatching("no region matches emit target 'does-not-exist'").length === 1;
 		out.t19_nobodyGotIt = kinds(p.received.b).indexOf('nowhere') < 0 && kinds(p.received.c).indexOf('nowhere') < 0;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/regions-declarative-default.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/regions-declarative-default.cjs
index 198e804048..2216ef339d 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/regions-declarative-default.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/regions-declarative-default.cjs
@@ -47,10 +47,10 @@ const out = {};
 // by different code paths that may set attributes in a different order), and children recursively - which is a
 // stricter, not a looser, check than a literal innerHTML byte-compare would be for two independently-built trees.
 function escapeText(s) {
-	return String(s == null ? '' : s).replaceAll(/&/g, '&amp;').replaceAll(/</g, '&lt;').replaceAll(/>/g, '&gt;');
+	return String(s == null ? '' : s).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
 }
 function escapeAttr(s) {
-	return escapeText(s).replaceAll(/"/g, '&quot;');
+	return escapeText(s).replaceAll('"', '&quot;');
 }
 function serializeNode(n) {
 	if (n.nodeType === 3) return escapeText(n.textContent);
@@ -162,7 +162,7 @@ function load(opts) {
 		});
 		const calls = H.abortableFetch(env);
 		R.initRegion(el); // the region's own R14a pre-fetch issues calls[0]
-		out.t13_fetchUrl = calls[0] && calls[0].url;
+		out.t13_fetchUrl = calls[0]?.url;
 		calls.resolve(0, { contractVersion: '1', fields: { status: 'ok', count: 3 } });
 		await H.flush();
 		out.t13_valuesMap = ctx.data; // the pre-fetch's own resolution, already unwrapped onto ctx.data
@@ -238,8 +238,8 @@ function load(opts) {
 		const body = bodyMatch ? bodyMatch[0] : '';
 		const fetchCallSites = (body.match(/fetchDeclared\(\)/g) || []).length;
 		out.t16g_oneFetchDeclaredCallSite = fetchCallSites;
-		out.t16g_guardedByNullish = /ctx\.data\s*!=\s*null[\s\S]*?:\s*ctx\.fetchDeclared\(\)/.test(body)
-			|| /ctx\.data\s*\?\?\s*[\s\S]*?fetchDeclared/.test(body);
+		out.t16g_guardedByNullish = /ctx\.data\s*!=\s*null[\s\S]*?:\s*ctx\.fetchDeclared\(\)/.test(body) // NOSONAR javascript:S5852 -- lazy scan of a small, fixed local source slice (the test's own input)
+			|| /ctx\.data\s*\?\?\s*[\s\S]*?fetchDeclared/.test(body); // NOSONAR javascript:S5852 -- lazy scan of a small, fixed local source slice (the test's own input)
 	}
 	{
 		// Behavioral half: pre-set ctx.data (as R14a's own pre-fetch would), call the default directly, and count
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/regions-harness.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/regions-harness.cjs
index 3bc616f2b6..b5488c1375 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/regions-harness.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/regions-harness.cjs
@@ -139,7 +139,7 @@ function load(rendersJsPath, viewsJsPath, regionsJsPath, opts) {
 		// (and, when supplied, juneau-helpers.js) sources into a VM sandbox is this harness's
 		// intended mechanism for exercising them under the DOM shim; inputs are fixed local file paths supplied by
 		// the test, never attacker-controlled data.
-		vm.runInNewContext(fs.readFileSync(path.resolve(file), 'utf8'), sandbox, { filename: path.basename(file) });
+		vm.runInNewContext(fs.readFileSync(path.resolve(file), 'utf8'), sandbox, { filename: path.basename(file) }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 	}
 
 	const NS = env.window.JuneauViews;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/regions-mount.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/regions-mount.cjs
index 52142f3d6b..1c1ad3d513 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/regions-mount.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/regions-mount.cjs
@@ -43,7 +43,7 @@ function slot(env, id) {
 	return el;
 }
 
-(async function () {
+(async function () { // NOSONAR javascript:S3776 -- linear test scenario
 
 	// =================================================================================================================
 	// Happy path: mount stamps enrolment attrs, enrols, and populate fills the BODY only.
@@ -143,7 +143,7 @@ function slot(env, id) {
 		let threw = false;
 		try {
 			R.mount({ probes: '  ' });
-		} catch (error) {
+		} catch (error) { // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 			threw = true;
 		}
 		out.t4_threw = threw;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/regions-primitive.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/regions-primitive.cjs
index 8ddbf57502..4e8cd181f6 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/regions-primitive.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/regions-primitive.cjs
@@ -42,7 +42,7 @@ function stateOf(el) {
 	return el.dataset.juneauRegionState ?? null;
 }
 
-(async function () {
+(async function () { // NOSONAR javascript:S3776 -- linear test scenario
 
 	// =================================================================================================================
 	// Test 1 - the container is EMPTY and IN-DOCUMENT at the first call, for each of the three region types.
@@ -340,11 +340,9 @@ function stateOf(el) {
 	// =================================================================================================================
 	{
 		const { env, R } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
-		let ctx = null;
 		let topLevelSignal = null;
 		let forkSignal = null;
 		R.register('fetcher', function (c) {
-			ctx = c;
 			topLevelSignal = c.signal;
 			forkSignal = c.signal.fork();
 		});
@@ -450,7 +448,7 @@ function stateOf(el) {
 	{
 		const { env, R, rec } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
 		const reasons = {};
-		R.register('noter', function (ctx) { (reasons[ctx.id] = reasons[ctx.id] || []).push(ctx.reason); });
+		R.register('noter', function (ctx) { reasons[ctx.id] = reasons[ctx.id] || []; reasons[ctx.id].push(ctx.reason); });
 		const scope = env.el('div');
 		env.body.appendChild(scope);
 		const visible = H.mkRegion(env, { id: 'vis', type: 'card-body', populate: 'noter', parent: scope });
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/renders.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/renders.cjs
index 3635b71c45..3917dc2ed6 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/renders.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/renders.cjs
@@ -51,7 +51,7 @@ sandbox.window = sandbox;
 sandbox.globalThis = sandbox;
 // NOSONAR javascript:S1523 -- this is the test harness deliberately loading the real
 // juneau-renders.js under test into an isolated vm sandbox; there is no untrusted input.
-vm.runInNewContext(code, sandbox);
+vm.runInNewContext(code, sandbox); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 
 const NS = sandbox.window.JuneauViews;
 const R = NS._render;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-dialog.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-dialog.cjs
index e092e858e2..badc8a6c02 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-dialog.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-dialog.cjs
@@ -73,7 +73,7 @@ function loadBoth() {
 	};
 	// NOSONAR javascript:S1523 -- loading the production juneau-ribbon.js source into a VM sandbox is this
 	// harness's intended mechanism; the path is a fixed local file supplied by the test.
-	vm.runInNewContext(ribbonJsSource, sandbox, { filename: 'juneau-ribbon.js' });
+	vm.runInNewContext(ribbonJsSource, sandbox, { filename: 'juneau-ribbon.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 	return { env: env, NS: env.window.JuneauViews, I: loaded.I };
 }
 
@@ -316,7 +316,7 @@ function anyRowBanner(env) {
 		await flush();
 
 		out.mergeRowless_redrawCalledOnce = fx.ctx.redrawCount() === 1;
-		out.mergeRowless_outcomeStillRendered = (ribbonOutcome(env) || {}).dataset?.state === 'success';
+		out.mergeRowless_outcomeStillRendered = ribbonOutcome(env)?.dataset?.state === 'success';
 	})();
 
 	process.stdout.write(JSON.stringify(out));
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-icon-fallback.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-icon-fallback.cjs
index 66aacb5e8a..858baf3c3a 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-icon-fallback.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-icon-fallback.cjs
@@ -48,7 +48,7 @@ function probe(resolveIcon) {
 		setInterval: function () { return 0; }, clearInterval: function () { /* no-op */ }
 	};
 	// NOSONAR javascript:S1523 -- loading a production JS source into a VM sandbox is this harness's intended mechanism; the input is a fixed local file supplied by the test.
-	vm.runInNewContext(ribbonJsSource, sandbox, { filename: 'juneau-ribbon.js' });
+	vm.runInNewContext(ribbonJsSource, sandbox, { filename: 'juneau-ribbon.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 	const NS = env.window.JuneauViews;
 	NS.icons = { resolveIcon: resolveIcon };
 	const bar = NS.ribbon.build({ ribbon: [{ type: 'refresh', title: 'Reload it' }] }, { dataTable: {}, redraw: function () { /* no-op */ } });
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-normalize.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-normalize.cjs
index 3fbb40606f..be6b13ad51 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-normalize.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/ribbon-normalize.cjs
@@ -67,7 +67,7 @@ function loadRibbon(withExportFeature) {
 		setInterval: function () { return 0; }, clearInterval: function () { /* no-op */ }
 	};
 	// NOSONAR javascript:S1523 -- loading a production JS source into a VM sandbox is this harness's intended mechanism; the input is a fixed local file supplied by the test.
-	vm.runInNewContext(ribbonJsSource, sandbox, { filename: 'juneau-ribbon.js' });
+	vm.runInNewContext(ribbonJsSource, sandbox, { filename: 'juneau-ribbon.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 	return { env: env, NS: env.window.JuneauViews };
 }
 
@@ -295,7 +295,7 @@ function buildBar(NS, ribbon) {
 	const btn = groups.length > 0 ? groups[0].childNodes[0] : null;
 	out.dom_dialog_noViewRuntime_buttonRendered = btn != null;
 	let threw = false;
-	try { if (btn) btn.dispatch('click'); } catch (error) { threw = true; }
+	try { if (btn) btn.dispatch('click'); } catch (error) { threw = true; } // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 	out.dom_dialog_noViewRuntime_clickDidNotThrow = ! threw;
 }
 
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/row-action-endpoint.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/row-action-endpoint.cjs
index 5aafa23b7a..cbf522eae1 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/row-action-endpoint.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/row-action-endpoint.cjs
@@ -57,13 +57,13 @@ sandbox.globalThis = sandbox;
 // NOSONAR javascript:S1523 -- this harness's entire purpose is to load the production runtime under test (a
 // repo-local file path from argv, not attacker-controlled input) into an isolated VM sandbox; that IS the test.
 if (rendersJsPath)
-	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' });
+	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 // NOSONAR javascript:S1523 -- same rationale: loading the production juneau-views.js under test into the sandbox.
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 
 const NS = window.JuneauViews;
 const I = NS?.init;
-const out = { hasInit: !!(typeof I?.buildActionRequest === 'function') };
+const out = { hasInit: typeof I?.buildActionRequest === 'function' };
 if (!out.hasInit) {
 	process.stdout.write(JSON.stringify(out));
 	process.exit(0);
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/row-action-form.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/row-action-form.cjs
index deeea7cf3b..63fab58df4 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/row-action-form.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/row-action-form.cjs
@@ -55,7 +55,7 @@ function loadViewsOnly(env) {
 		fetch: function (...args) { return env.callFetch(...args); }
 	};
 	// NOSONAR javascript:S1523 -- loading the production juneau-views.js under test into the sandbox.
-	vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+	vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 	const NS = env.window.JuneauViews;
 	return { env: env, NS: NS, I: NS?.init };
 }
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/row-actions.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/row-actions.cjs
index 15b7d475b7..36b8756673 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/row-actions.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/row-actions.cjs
@@ -72,7 +72,7 @@ const PROBE = function () {
 
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page.
-	function makeTable(tokenValue) {
+	function makeTable(tokenValue) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		const table = document.createElement('table');
 		table.dataset.juneauView = 'v';
 		if (tokenValue != null) table.dataset.juneauCsrf = tokenValue;
@@ -89,7 +89,7 @@ const PROBE = function () {
 
 	// NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) ships only PROBE's own source into the
 	// browser context, so a helper hoisted to this file's Node module scope would be undefined in the page.
-	function rendered(el) {
+	function rendered(el) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		if (!el) return false;
 		const r = el.getBoundingClientRect();
 		return r.width > 0 && r.height > 0;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/row-detail.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/row-detail.cjs
index 2130c6f580..39ded85787 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/row-detail.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/row-detail.cjs
@@ -39,7 +39,7 @@ if (!viewsJsPath) {
 
 /** Minimal CSS-selector matcher: tag, `.class`, `[attr]` / `[attr="v"]`, comma lists, and tag[attr] combos. */
 function elMatches(node, sel) {
-	if (!node || node.nodeType !== 1) return false;
+	if (node?.nodeType !== 1) return false;
 	if (sel.indexOf(',') >= 0)
 		return sel.split(',').some(function (part) { return elMatches(node, part.trim()); });
 	let rest = sel;
@@ -214,7 +214,7 @@ function parseAttrs(raw, node) {
 	// unquoted HTML attribute values for the test-fixture HTML parser below; collapsing the 3-way alternation
 	// (e.g. via a backreference to the opening quote) would change which characters are permitted inside quoted
 	// values with no behavior-preserving equivalent, so it is left as-is.
-	const re = /([:@\w-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;
+	const re = /([:@\w-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g; // NOSONAR javascript:S5843 -- see above: three attribute-value forms
 	let m;
 	for (m = re.exec(raw); m; m = re.exec(raw))
 		node.setAttribute(m[1], firstDefined(m[2], m[3], m[4]));
@@ -268,13 +268,13 @@ const rendersJsPath = process.argv[3];
 // NOSONAR javascript:S1523 -- this harness's entire purpose is to load the production runtime under test (a
 // repo-local file path from argv, not attacker-controlled input) into an isolated VM sandbox; that IS the test.
 if (rendersJsPath)
-	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' });
+	vm.runInNewContext(fs.readFileSync(path.resolve(rendersJsPath), 'utf8'), sandbox, { filename: 'juneau-renders.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 // NOSONAR javascript:S1523 -- same rationale: loading the production juneau-views.js under test into the sandbox.
-vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' }); // NOSONAR javascript:S1523 -- the harness evaluates the module's own bundled script
 
 const NS = window.JuneauViews;
 const I = NS?.init;
-const out = { hasInit: !!(typeof I?.fillDetailSlots === 'function') };
+const out = { hasInit: typeof I?.fillDetailSlots === 'function' };
 if (!out.hasInit) {
 	process.stdout.write(JSON.stringify(out));
 	process.exit(0);
@@ -436,13 +436,13 @@ function markdownSlot() {
 	return s;
 }
 function collectTags(node, acc) {
-	if (!node || node.nodeType !== 1) return acc;
+	if (node?.nodeType !== 1) return acc;
 	acc.push(node.tagName);
 	for (const c of node.childNodes) collectTags(c, acc);
 	return acc;
 }
 function findTag(node, tag, list) {
-	if (!node || node.nodeType !== 1) return list;
+	if (node?.nodeType !== 1) return list;
 	if (node.tagName === tag) list.push(node);
 	for (const c of node.childNodes) findTag(c, tag, list);
 	return list;
@@ -503,7 +503,7 @@ out.href_fragment = I.isSafeMarkdownHref('#frag');
 
 /** Every attribute name present anywhere in the painted subtree - the global "no handler survived" sweep. */
 function collectAttrNames(node, acc) {
-	if (!node || node.nodeType !== 1) return acc;
+	if (node?.nodeType !== 1) return acc;
 	for (const k in node.attrs || {}) acc.push(k.toLowerCase());
 	for (const c of node.childNodes) collectAttrNames(c, acc);
 	return acc;
@@ -598,12 +598,12 @@ let deepThrew = false;
 let deepResult = null;
 try {
 	deepResult = sanitizedPaint(deepHtml);
-} catch (error) {
+} catch (error) { // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 	deepThrew = true;
 }
 out.sh_deepNesting_doesNotThrow = !deepThrew;
 function maxElementDepth(node) {
-	if (!node || node.nodeType !== 1 || !node.childNodes || node.childNodes.length === 0) return 0;
+	if (node?.nodeType !== 1 || !node.childNodes?.length) return 0;
 	let m = 0;
 	for (const c of node.childNodes) m = Math.max(m, maxElementDepth(c));
 	return 1 + m;
@@ -678,7 +678,7 @@ out.sh_nullClears = blank.childNodes.length === 0;
 // 6. isSafeImageSrc directly.
 out.shSrc_https = I.isSafeImageSrc('https://x/p.png');
 // NOSONAR javascript:S5332 -- fixture URL, not a production endpoint.
-out.shSrc_http = I.isSafeImageSrc('http://x/p.png');
+out.shSrc_http = I.isSafeImageSrc('http://x/p.png'); // NOSONAR javascript:S5332 -- fixture URL, never fetched
 out.shSrc_data = I.isSafeImageSrc('data:image/png;base64,AAA');
 out.shSrc_js = I.isSafeImageSrc('javascript:alert(1)');
 out.shSrc_vbscript = I.isSafeImageSrc('vbscript:msgbox(1)');
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/row-selection-bulk.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/row-selection-bulk.cjs
index 8fbb502404..588f604134 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/row-selection-bulk.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/row-selection-bulk.cjs
@@ -97,7 +97,7 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- stays nested inside PROBE: page.evaluate() serializes this function
 	// source across the Playwright process boundary with no access to outer Node-module scope, so it
 	// cannot be hoisted to module level without breaking in-browser execution.
-	function fakeDt() {
+	function fakeDt() { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		const handlers = {};
 		return {
 			on: function (evt, cb) {
@@ -110,7 +110,7 @@ const PROBE = async function () {
 
 	// NOSONAR javascript:S7721 -- stays nested inside PROBE for the same cross-process-boundary reason
 	// as fakeDt() above.
-	function dispatchChange(el) {
+	function dispatchChange(el) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		el.dispatchEvent(new Event('change', { bubbles: true }));
 	}
 
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/sectioned-dialog-browser.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/sectioned-dialog-browser.cjs
index c470a3de45..8389daf275 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/sectioned-dialog-browser.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/sectioned-dialog-browser.cjs
@@ -50,11 +50,11 @@ const PROBE = async function () {
 	// NOSONAR javascript:S7721 -- shown()/makeRow()/parts() below stay nested inside PROBE even though they
 	// close over no outer locals: page.evaluate() serializes only PROBE's own source text across the
 	// Playwright process boundary, so a module-scope sibling would be undefined inside the browser.
-	function shown(el) { if (!el) { return false; } const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; }
+	function shown(el) { if (!el) { return false; } const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; } // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 	function drain() { while (init.topLayer()) init.popLayer(); }
 	function displayOf(el) { return el ? window.getComputedStyle(el).display : null; }
 
-	function makeRow(rowId) {
+	function makeRow(rowId) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		const table = document.createElement('table');
 		const tbody = document.createElement('tbody');
 		const tr = document.createElement('tr');
@@ -94,7 +94,7 @@ const PROBE = async function () {
 	}
 
 	// NOSONAR javascript:S7721 -- same cross-process-boundary reason as shown()/makeRow() above.
-	function parts() {
+	function parts() { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate(PROBE) serializes only PROBE's own source into the browser
 		const backdrop = document.querySelector('.juneau-view-dialog-backdrop');
 		const wrap = backdrop ? backdrop.querySelector('.juneau-view-dialog-form') : null;
 		const strip = wrap ? wrap.querySelector('[data-testid="dialog-sections"]') : null;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/slot-mount.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/slot-mount.cjs
index f543e3f842..8a6dfdc8a2 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/slot-mount.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/slot-mount.cjs
@@ -57,7 +57,7 @@ function envelope(NS, extra) {
 	return out;
 }
 
-(async function () {
+(async function () { // NOSONAR javascript:S3776 -- linear test scenario
 	const out = {};
 
 	// =================================================================================================================
@@ -84,7 +84,7 @@ function envelope(NS, extra) {
 	// URL fetch; 500 / malformed / version mismatch banner in THAT slot; sibling region stays.
 	// =================================================================================================================
 	{
-		const { env, NS, R, rec } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
+		const { env, R, rec } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
 		R.register('ssc-probes', function (ctx, container) {
 			container.appendChild(env.el('span'));
 		});
@@ -109,7 +109,7 @@ function envelope(NS, extra) {
 	}
 
 	{
-		const { env, NS, R, rec } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
+		const { env, R, rec } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
 		const incidents = slot(env, 'incidents');
 		env.setFetch(function () { return Promise.resolve(H.jsonResponse('not-json{')); });
 		await Promise.resolve(R.mount({ incidents: { table: '/releases/slot' } }));
@@ -178,7 +178,7 @@ function envelope(NS, extra) {
 		let threw = false;
 		try {
 			R.mount({ incidents: { table: '   ' } });
-		} catch (error) {
+		} catch (error) { // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 			threw = true;
 		}
 		out.t7_wsThrew = threw;
@@ -195,7 +195,7 @@ function envelope(NS, extra) {
 		let threw = false;
 		try {
 			R.mount({ probes: 'ssc-probes', incidents: { table: envelope(NS) } });
-		} catch (error) {
+		} catch (error) { // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 			threw = true;
 		}
 		out.t8_missingIdThrew = threw;
@@ -210,7 +210,7 @@ function envelope(NS, extra) {
 		let threw = false;
 		try {
 			R.mount({ probes: 'ssc-probes', incidents: { foo: 1 } });
-		} catch (error) {
+		} catch (error) { // NOSONAR javascript:S2486 -- the thrown/not-thrown outcome is recorded in a flag and asserted on
 			threw = true;
 		}
 		out.t9_badShapeThrew = threw;
@@ -295,7 +295,7 @@ function envelope(NS, extra) {
 		let declared = null;
 		try {
 			declared = region ? JSON.parse(region.dataset.juneauRegionDeclared) : null;
-		} catch (error) { declared = null; }
+		} catch (error) { declared = null; } // NOSONAR javascript:S2486 -- an unparsable declaration is recorded as null and asserted on
 		out.t12_declaredDataUrlOnly = !!(declared?.dataUrl === '/data/{id}'
 			&& Object.keys(declared).length === 1);
 		out.t12_noRegionMeta = incidents.querySelector('[data-juneau-region-meta]') == null
@@ -425,8 +425,7 @@ function envelope(NS, extra) {
 		out.t17_hasTable = table != null;
 		out.t17_noFetch = fetched === 0;
 		out.t17_rowsStashed = table?.__juneauRows === rows;
-		out.t17_rowIdField = table?.getAttribute('data-juneau-row-id-field') === 'id'
-			|| table?.dataset.juneauRowIdField === 'id';
+		out.t17_rowIdField = table?.dataset.juneauRowIdField === 'id';
 		out.t17_noDomRows = table?.querySelector('tbody') == null;
 		const opts = NS.init.buildOptions(envl.view, {
 			table: table, parseRenderId: NS.parseRenderId, resolveRenderer: NS.resolveRenderer, warn: function () {}
@@ -458,7 +457,7 @@ function envelope(NS, extra) {
 		env.window.location = { href: 'https://app.example.com/ctx/page', origin: 'https://app.example.com' };
 		function mountOne(id, extra) {
 			const el = slot(env, id);
-			return Promise.resolve(R.mount({ [id]: { table: envelope(NS, Object.assign({ viewId: 'v' + id }, extra)) } })).then(function () {
+			return Promise.resolve(R.mount({ [id]: { table: envelope(NS, { viewId: 'v' + id, ...extra }) } })).then(function () {
 				return {
 					saved: el.querySelector('[data-juneau-slot-table]')?.dataset.juneauSavedViews,
 					detail: el.querySelector('template[data-juneau-row-detail]')?.dataset.juneauDetailUrl
@@ -485,6 +484,19 @@ function envelope(NS, extra) {
 			&& rec.warnsMatching('detail.endpoint').length === 2;
 	}
 
+	// =================================================================================================================
+	// The document-wide table scan leaves a slot-built table alone: the envelope is its definition (no sidecar), and
+	// the slot mount already inits it.
+	// =================================================================================================================
+	{
+		const { env, NS, R, rec } = H.load(rendersJsPath, viewsJsPath, regionsJsPath);
+		const incidents = slot(env, 'incidents');
+		await Promise.resolve(R.mount({ incidents: { table: envelope(NS) } }));
+		out.t19_tableMounted = incidents.querySelector('table[data-juneau-view="releases"]') != null;
+		NS.init.initAll();
+		out.t19_scanLogsNoMissingSidecar = rec.errorsMatching('missing JSON sidecar').length === 0;
+	}
+
 	process.stdout.write(JSON.stringify(out));
 })().catch(function (error) {
 	process.stderr.write(String(error?.stack ? error.stack : error));
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/symbol-sprite-render.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/symbol-sprite-render.cjs
index 79383aa121..38f65e1b78 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/js/symbol-sprite-render.cjs
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/symbol-sprite-render.cjs
@@ -265,7 +265,7 @@ const PIXEL_DIFF = async function (args) {
 			img.src = dataUrl;
 		});
 	};
-	const toLum = function (img) {
+	const toLum = function (img) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate() serializes only the evaluated function's own source into the browser
 		const c = document.createElement('canvas');
 		c.width = img.naturalWidth;
 		c.height = img.naturalHeight;
@@ -291,7 +291,7 @@ const PIXEL_DIFF = async function (args) {
 
 /** Composites a sheet from already-rendered tiles.  Pure canvas drawing; no measurement happens here. */
 const COMPOSE = async function (spec) {
-	const load = function (dataUrl) {
+	const load = function (dataUrl) { // NOSONAR javascript:S7721 -- must stay nested: page.evaluate() serializes only the evaluated function's own source into the browser
 		return new Promise(function (resolve, reject) {
 			const i = new Image();
 			i.onload = function () { resolve(i); };
@@ -349,7 +349,7 @@ async function shoot(page) {
 	return 'data:image/png;base64,' + buf.toString('base64');
 }
 
-(async () => {
+(async () => { // NOSONAR javascript:S3776 -- linear test scenario
 	const [fixture, requestJson] = process.argv.slice(2);
 	if (!fixture || !requestJson) {
 		process.stderr.write('usage: node symbol-sprite-render.cjs <fixture.html> <request-json>\n');
@@ -419,15 +419,19 @@ async function shoot(page) {
 		sheetOps.push({ op: 'text', text: legend, x: 12, y: 38, font: '11px sans-serif', color: INK });
 		stems.forEach(function (stem, i) {
 			const top = headerH + i * rowH;
-			sheetOps.push({ op: 'rule', x1: 0, x2: magX12 + small * MAG + 12, y1: top, color: RULE });
-			sheetOps.push({ op: 'text', text: stem, x: 12, y: top + rowH / 2 + 4, font: '12px sans-serif', color: INK });
+			sheetOps.push(
+				{ op: 'rule', x1: 0, x2: magX12 + small * MAG + 12, y1: top, color: RULE },
+				{ op: 'text', text: stem, x: 12, y: top + rowH / 2 + 4, font: '12px sans-serif', color: INK }
+			);
 			sizes.forEach(function (size) {
 				const t = tiles[stem][size];
 				sheetOps.push({ op: 'img', dataUrl: t.dataUrl, x: colX[size], y: top + (rowH - size) / 2 });
 			});
-			sheetOps.push({ op: 'img', dataUrl: tiles[stem][ribbon].magnified, x: magX16, y: top + ROW_PAD });
-			sheetOps.push({ op: 'img', dataUrl: tiles[stem][small].magnified, x: magX12,
-				y: top + ROW_PAD + (ribbon - small) * MAG / 2 });
+			sheetOps.push(
+				{ op: 'img', dataUrl: tiles[stem][ribbon].magnified, x: magX16, y: top + ROW_PAD },
+				{ op: 'img', dataUrl: tiles[stem][small].magnified, x: magX12,
+					y: top + ROW_PAD + (ribbon - small) * MAG / 2 }
+			);
 		});
 		writePng(path.join(req.outputDir, 'sheet.png'), await page.evaluate(COMPOSE, {
 			width: magX12 + small * MAG + 16,
@@ -459,19 +463,23 @@ async function shoot(page) {
 			+ ribbon + 'px, magnified ' + MAG + 'x', x: 12, y: 22, font: 'bold 14px sans-serif', color: INK });
 		req.family.forEach(function (stem, i) {
 			const left = 12 + i * famColW;
-			famOps.push({ op: 'text', text: stem, x: left, y: 44, font: '12px sans-serif', color: INK });
-			famOps.push({ op: 'img', dataUrl: tiles[stem][ribbon].dataUrl, x: left, y: 52 });
-			famOps.push({ op: 'img', dataUrl: tiles[stem][ribbon].magnified, x: left, y: 52 + ribbon + 8 });
+			famOps.push(
+				{ op: 'text', text: stem, x: left, y: 44, font: '12px sans-serif', color: INK },
+				{ op: 'img', dataUrl: tiles[stem][ribbon].dataUrl, x: left, y: 52 },
+				{ op: 'img', dataUrl: tiles[stem][ribbon].magnified, x: left, y: 52 + ribbon + 8 }
... 411 lines suppressed ...