# RC108 · Exercise library real-DOM compactness QA

## Purpose

RC108 is a targeted verification/fix release on top of RC107. The goal is to clean the exercise library expanded state using the real browser DOM produced by the existing library renderer, not a static fixture.

No new exercise images were generated. Image generation remains paused.

## Reference implementation carried forward

Read before change:

- `docs/ARCHITECTURE_DECISIONS.md`
- `docs/DO_NOT_TOUCH_WITHOUT_DESIGN.md`
- `docs/RC104_DIRECT_COMPOSITE_CLEANUP_NOTES.md`
- `docs/RC106_LIBRARY_CSS_THEME_CLEANUP.md`
- `docs/RC107_EXERCISE_LIBRARY_ASSET_CLEANUP.md`
- `docs/EXERCISE_LIBRARY_VIDEO_STATUS_RC95.md`

Carried-forward implementation:

- Static `preview.html` shell with existing script order.
- Existing exercise library service/render path:
  - `assets/js/services/exercise-library-service.js`
  - `assets/js/actions/library-actions.js`
  - `assets/js/renderers/library-renderer.js`
- Existing illustration path:
  - `assets/js/core/exercise-illustrations.js`
  - `assets/js/services/exercise-illustration-service.js`
- Existing theme path:
  - `assets/js/services/theme-service.js`
- Existing server/local backup services:
  - `assets/js/services/server-video-backup-service.js`
  - `assets/js/services/local-video-backup-service.js`

No new renderer, fallback SVG system, background model, or player subsystem was introduced.

## Classes/assets verified and reused

RC108 keeps the RC107 expanded component classes as the styling contract:

- `.next-library-detail-inline`
- `.next-library-media-panel`
- `.next-library-visual-panel`
- `.next-library-video-panel`
- `.next-library-video`
- `.next-library-copy-panel`
- `.next-library-backup-panel`
- `.next-library-facts`
- `.next-library-dose-grid`
- `.next-library-pass-uses`
- `.next-exercise-illustration`
- `.next-exercise-illustration-stage`
- `.next-exercise-illustration-composite`
- `.next-premium-composite`

Premium direct-composite path verified on expanded Bear Crawl/Björngång:

```text
assets/img/exercises/premium/s2p1/bear-crawl.webp
```

The DOM reports:

```text
data-quality="premium-approved"
data-background-mode="composite"
data-theme="hockey"
```

This confirms that the S2P1 premium image is still using the direct-composite route, not the paused foreground/background split.

Theme backgrounds remain in the active theme background structure:

```text
assets/img/themes/exercise-backgrounds/
```

## RC108 changes

### CSS compactness / expanded card

Changed only through existing class ownership in `assets/css/next-system.css`:

- Compact closed exercise rows.
- Reduced expanded card gaps, padding and overlarge boxes.
- Kept iPad/tablet portrait in two-column layout from 760px and up.
- Restored dark readable expanded-card surface for the actual DOM.
- Made premium approved/pilot images use `object-fit: contain` to avoid cropping direct-composite illustrations.
- Hid idle server backup upload status until an upload state is active.
- Reduced facts/cards/pass-use density.
- Kept empty/OTA/missing video placeholders compact unless a real iframe is inserted.

### Renderer adjustments

Changed only inside the existing `assets/js/renderers/library-renderer.js` component:

- Backup panel no longer repeats the exercise title inside the panel.
- Backup upload status starts with `.is-idle` so CSS can hide the idle block.
- Pass-use list is limited to 4 entries in expanded cards.
- Coach cues are limited to 4 bullets in expanded cards.
- Existing `.next-library-video` shell receives a source class:
  - `.next-library-video-source-youtube`
  - `.next-library-video-source-ota`
  - `.next-library-video-source-server`
  - `.next-library-video-source-missing`

This source class is only used for compact CSS targeting. It does not introduce a new video model.

## Browser/DOM QA method

Direct browser navigation to local `file://` and local `http://127.0.0.1` was blocked by Chromium in this sandbox with `ERR_BLOCKED_BY_ADMINISTRATOR`.

To still test the real renderer/DOM/cascade, QA used Chromium + Playwright with `page.set_content()` after inlining the actual `preview.html`, CSS and scripts. This allowed the real `window.NEXTRenderers.library.renderDetail()` output and real CSS cascade to be inspected.

Known QA limitation: because the test document runs as `about:blank`, local file asset loading is blocked for screenshots. For screenshots only, local images visible in the rendered DOM were injected as data URIs. The measured DOM still records the original production Bear Crawl image source as:

```text
assets/img/exercises/premium/s2p1/bear-crawl.webp
```

OTA proxy success was mocked in the browser QA to verify that `renderLibraryOtaYoutubePreview()` replaces the OTA placeholder with an inline YouTube iframe. Actual OTA proxy/network behavior must be verified on a deployed host.

## QA results

Screenshots created:

- `qa_screenshots/rc108_library_expanded_mobile_390x844.png`
- `qa_screenshots/rc108_library_expanded_ipad_820x1180.png`
- `qa_screenshots/rc108_library_expanded_desktop_1440x1024.png`

DOM metrics saved:

- `qa_screenshots/rc108_library_expanded_dom_metrics.json`

Screenshot/DOM QA script:

- `tests/rc109_library_real_dom_qa.py` supersedes the RC108 script for the current package

### Mobile · 390 × 844

Bear Crawl/Björngång expanded card:

```text
rowCount: 172
detailHeight: 1114.5px
gridTemplateColumns: 342px
videoSize: 342 × 170px
copyHeight: 681.875px
imageRenderedNaturalSize: 1659 × 948
imageFit: contain
imageOriginalSrc: assets/img/exercises/premium/s2p1/bear-crawl.webp
backupIdleDisplay: none
passUseCount: 4
passMoreText: + 4 fler passkopplingar
brokenImages: []
```

### iPad/tablet · 820 × 1180

```text
detailSize: 770.8 × 684.2px
gridTemplateColumns: 362.016px 376.797px
videoSize: 362 × 190px
copySize: 376 × 662px
```

### Desktop · 1440 × 1024

```text
detailSize: 1040 × 673px
gridTemplateColumns: 493.906px 514.094px
videoSize: 493 × 190px
```

### YouTube inline path

Direct YouTube exercise verified using Dynamic Warm-up:

```text
videoShellClass: next-library-video next-library-video-source-youtube
iframeSrc: https://www.youtube.com/embed/X_OS_EtOHZ4?rel=0&controls=1&playsinline=1
```

OTA proxy path verified with mocked proxy success:

```text
iframeSrc: https://www.youtube.com/embed/TESTID12345?rel=0&modestbranding=1
```

## Automated checks

Run in RC108:

```bash
node tests/rc107_asset_and_library_smoke.js
node tests/rc109_library_compactness_static_smoke.js
python3 tests/rc109_library_real_dom_qa.py
find assets/js -name '*.js' -print0 | xargs -0 -n1 node --check
php -l ota_youtube_proxy.php
php -l video_backup_api.php
```

Result: all checks passed.

## Explicit non-changes

- No new generated images.
- No foreground/background cutout workflow reactivated.
- No new asset model.
- No new parallel renderer.
- No pass player/video lifecycle changes.
- No SVG fallback system.
- No changes to RC104 S2P1 direct-composite mapping.
