← all documents · raw markdown · 23 KB

BIGPICTURE-GAP.md — what the Oracle Arcade hub actually renders, measured

Written 2026-08-12. Measurement pass only — no product code was written or changed.

This document is numbers. Where a reference figure is an estimate it says so. Where a previous

claim turned out to be wrong, it says that too.

---

0. How these numbers were obtained (so they can be reproduced or disputed)

The dashboard could not be used. [internal service]/ returns HTTP 200 but serves

<title>Oracle OS — Sign In</title> — the login wall (CC_CONTROL_TOKEN is set;

command-center-server.mjs mints a cc_session cookie only after POST [internal endpoint]).

Authenticating was out of scope, so the page was loaded another way.

Electron is not installed anywhere under [local path] (checked all six node_modules trees).

The substitute is Puppeteer 25.2.1 driving its own Chrome 150.0.7871.24 headless-shell

([local path]) — same Blink engine family as the

CEF that Big Picture itself runs in. Launched with deviceScaleFactor: 1, --hide-scrollbars,

--mute-audio, --force-device-scale-factor=1.

Harness: a read-only static server (scratchpad/serve.js) over [local path] on

[internal service], serving the *real on-disk* oracle.html and its real assets. /api/* returns

a stub JSON body. Every request to any other host is aborted, so the CDN three.js / katex /

Google-Fonts tags cannot stall the load — none of them are reachable from the Game Center.

Entry into the hub: window.GC.booted = true then GC.renderView() (this skips the

oracle-arcade.js boot splash and paints the library directly), then a 3.4 s settle before any

measurement.

Nothing here was modified by this pass. The only file this run created is

[local path]. SHA-1 of the shipped files, measured immediately before the probe run,

and again immediately after this document was written:

| file | before (what was measured) | after | |
|---|---|---|---|
| oracle-games.js | d20c02f893c6f199e9abe4051b0a6b8a05b3ae0d | 839b0cba187bfef995b37dc92e72cfc14d782ac8 | changed by the parallel workflow |
| oracle.css | f0ac22763dbb1217aabfa0896530b260e6249e0e | f0ac22763dbb1217aabfa0896530b260e6249e0e | unchanged |
| oracle.html | ec0ab839622925fe9be8ca0f5785cd1025b1ab9a | 0330e52f9ced9840e729bf6feed20b908e956d3c | changed by the parallel workflow |
| oracle-games-pack.js | 17229816cc39436ed0253d5e5d4fef850a9e2c27 | f4bb838980aa4f70154d52f2d4652784169820f1 | changed by the parallel workflow |

> ⚠ Every number below is pinned to the "before" column. Three of those four files were

> edited by the concurrent workflow *while this document was being written* — none of it by this

> pass (git status shows only ?? BIGPICTURE-GAP.md as this run's output). The CSS, which owns

> almost all of the geometry reported here, is byte-identical; the JS that builds the DOM is not.

> Re-run scratchpad/probe.js and confirm before acting on any single figure.

Scripts and screenshots (scratchpad, not in the repo):

serve.js, probe.js, probe2.js, probe3.js, pixels.js, show.js, wash.js, out/*.json,

out/*.png.

---

1. Headline: the two things a screenshot comparison would never have caught

1a. The focus model is real and it is good. Stop rebuilding it.

Driven with real keydown events through the CDP input pipe, not synthesised JS events:

| key | .gcbp-on index | label | metadata line | wash changed |
|---|---|---|---|---|
| start | 0 | Tic-Tac-Toe | Tic-Tac-Toe ↻ NEVER PLAYED | — |
| → | 1 | Connect Four | Connect Four ↻ NEVER PLAYED | yes |
| → | 2 | Reversi | Reversi ↻ NEVER PLAYED | yes |
| → | 3 | Gomoku | Gomoku ↻ NEVER PLAYED | yes |
| ↓ | 22 | Hex | Hex ↻ NEVER PLAYED | yes |
| ← | 21 | Nine Men's Morris | Nine Men's Morris ↻ NEVER PLAYED | yes |
| ↑ | 2 | Reversi | Reversi ↻ NEVER PLAYED | yes |

The wash's inline SVG data-URI hash changed on 6 of 6 moves (lengths 5606–6073).

removed). Escape returns.

Correction to a prior claim: the accent tint does *not* make every game's background look the

same. It is per-game and it changes on every focus move.

1b. On desktop, the first arrow-key press pans the entire console screen 210 px left, and it never comes back

.oa-shell has overflow: hidden but scrollWidth 2117 > clientWidth 1907, because the rail's

14 tiles overflow their layout box (.gcbp-rail is overflow-x: visible). The focus scroller

calls scrollIntoView, and Chromium scrolls hidden-overflow containers.

Measured, at 1907×1075, pressing twelve times:

| press | .oa-shell scrollLeft | hero x | heading x | tab strip x | bottom bar x | status x |
|---|---|---|---|---|---|---|
| (start) | 0 | 40 | 40 | 40 | 0 | 889.94 |
| 1 | 158 | −118 | −118 | −118 | −158 | 731.94 |
| 2 | 210 | −170 | −170 | −170 | −210 | 679.94 |
| 3–12 | 210 | −170 | −170 | −170 | −210 | 679.94 |

210 px is the clamp (2117 − 1907). It stays there for the rest of horizontal navigation and only

unwinds after four ↓ presses (210 → 210 → 159 → 159 → 1 → 0).

Visible result: a 210 px black dead band down the right edge, the search field sliced off the

left, and ORACLE pushed off the bottom-left of the persistent bar.

Evidence: out/deep-desktop-8right.png, out/p3-desktop-12right.png.

Mobile does not have this bug. At 375×812, .oa-shell scrollWidth 416 vs clientWidth 375,

scrollLeft clamps at 1 px, hero x stays 13–14, and the rail translates properly, holding the

focused tile at x ≈ 133 for all 12 presses.

---

2. Element-by-element, desktop 1907×1075

Reference column = the owner's screenshot geometry, cross-checked against Valve's published

capsule ratios. Items marked *(est)* have no published source.

Status cluster

| | ours | reference | delta |
|---|---|---|---|
| box | x 889.94, y 10, 228.14 × 44 | right-aligned, y ≈ 28 | — |
| right inset | 788.92 px (41.4 % of viewport) | ~40 px | not right-aligned |
| items | 5 | 6 | −1 |
| labels on icons | none (all 5 fail [A-Za-z]{3,}) | none | ✅ match |
| container chrome | background: transparent, border: 0, padding: 0 | none | ✅ match |
| gap | 3 px | — | — |

Items: 🔍 44×44 · 👥0 47.13×40 · 📶 40×40 · 07:05 53.02×40 (12 px/700/ls 0.6 px) · 🕹 32×32.

Host is .oa-shell-top (0,0,1907,65) with justify-content: space-between, so the cluster

occupies the middle slot between "ORACLE ARCADE" and "EXIT". That is why it is centred.

No battery/globe equivalent.

Section heading

| | ours | reference | delta |
|---|---|---|---|
| text | Game Center · solo · vs AI · online | Recent Games | — |
| position | x 40, y 99.5 | x ≈ 48, y ≈ 96 | ✅ within 8 px / 3.5 px |
| font-size | 13 px | ~28 px *(est)* | 46 % |
| style | 600, ls 1.82 px, uppercase, opacity 0.7, Outfit | large, sentence case *(est)* | wrong kind |

Left margin 40 px = 2.098 % of 1907 — the reference tiles also start at 40 px. **Exact match.

Do not move it.**

The row's own label is a separate element: Start here, 10 px, uppercase, ls 2.2 px, at

x = 869.88 (shoved right of the hero). Every other shelf heading is 10 px at x = 40.

The big landscape tile (.gc-hero)

| | ours | reference | delta |
|---|---|---|---|
| box | 803.88 × 306 | 798 × 375 | w +0.7 %, h 81.6 % |
| aspect | 2.627 | 2.128 (Library Header 920×430) | +23.4 % |
| CSS | flex: 0 0 44%, min-height: 290px, radius 14px | — | — |
| background | linear-gradient(120deg,#0a0f1b,rgba(9,14,24,.85)) | game art | not art |
| text on it | 104 characters | 0 | — |
| canvases / SVGs | 0 / 0 | — | — |

It is a split card, not a capsule. Two children:

glyph on a gradient. It is 45.9 % of the hero's width.

▶ Play / How to play.

So the *artwork* aspect in our landscape slot is 1.213 : 1, against a reference of

2.128 : 1 — 57 % of the target. The overall box width being within 0.7 % is coincidence.

It is also a flex sibling of the rail, not cell #1 of the row: .gcbp-top is

display:flex; gap:26px; align-items:stretch, hero then rail. There is no mixed-aspect row.

Cover tiles — the row

| | ours | reference | delta |
|---|---|---|---|
| tile size | 152.00 × 228.00 | 248 × 375 | w 61.3 %, h 60.8 % |
| aspect | 0.667 (aspect-ratio: 2/3 on .gcbp-art) | 0.661 | ✅ correct — keep |
| row height | 228 px = 21.21 vh | 375 px = 34.88 vh | 60.8 % |
| gap | 14 px | 10 px | +40 % |
| pitch | 166 px | 258 px | 64.3 % |
| first tile left | 869.88 | 848 | +21.9 |
| tiles across the rail band | 6.01 (997.13 / 166) | 4.10 (1059 / 258) | 1.47× denser |
| fully visible | 6 | ~4 | — |
| sliced at right edge | 1 | 1 | ✅ match |
| last tile right edge | 3179.88 (viewport 1907) | off-screen | ✅ bleeds |
| heights uniform | true — all 14 are 228 | mixed-aspect at one height | no mixed row |

The cover source art is already big enough. Decoded, each cover's SVG is intrinsically

300 × 450 — 1.97× the 152 px it is displayed at. Growing the row to 250 × 375 requires no new

art authoring.

Focused-item title and metadata

| | ours | reference | delta |
|---|---|---|---|
| container | .gcbp-meta.gcbp-meta-on at x 869.88, y 419, 997.13 × 38 | under the row | ✅ in kind |
| title b | 21 px / 800 / ls 0.42 px / uppercase | ~30 px *(est)* | 70 % |
| metadata span | 10.5 px / 600 / ls 1.68 px / JetBrains Mono / #6b7a90 | ~12 px *(est)* | 88 % |
| layout | side by side on one line — span starts 165.42 px right of the title's left edge; vertical top delta only 11 px | stacked — title y ≈ 535, metadata y ≈ 562, 27 px below | wrong structure |

The small-caps mono treatment itself is right; the stacking is not. There are 7 .gcbp-meta

elements in the document, one per shelf; exactly one carries .gcbp-meta-on.

Tab strip

| | ours | reference | delta |
|---|---|---|---|
| centred? | justify-content: center; centreOffset 0.00, itemsCentreOffset 0.00 | centred | ✅ exact |
| pill shape | border-radius: 999px, active fill rgb(34,217,230), 44 px tall, padding 0 17px, 11.5 px | filled pill | ✅ in kind |
| items | 10, spanning 1305.84 px | 3 | 3.3× |
| function | filtering (6 categories + 1px separator + 4 modes) | section switching | different job |

Items: ALL 34 (active) · BOARD & STRATEGY 9 · ARCADE 7 · PUZZLE 10 · WORD & TRIVIA 2 ·

REFLEX & SKILL 6 · sep · ANY MODE (active) · SOLO · VS AI · ONLINE.

The count badges the reference shows on FRIENDS are present on ours. .gcbp-tabs-anchor exists

but measures 0 × 0 at (0,0).

The content row below the tabs — does not exist

The reference has a row of ~430 px cards: eyebrow (NEWS, MAJOR UPDATE) above the art, then

art, then POSTED <date>, then title, bleeding off the right edge.

What is at that position instead:

| y | element | height | % of the 1075 screen |
|---|---|---|---|
| 85 | .gc-head | 44 | 4.1 % |
| 151 | .gcbp-top (hero + rail + meta) | 306 | 28.5 % |
| 481 | .gc-chips.gcbp-tabs | 64 | 6.0 % |
| 567 | .gc-prog-host — XP bar, daily challenge, "closest achievements" list | 475.75 | 44.3 % |

The largest single element on the first screen is a stats panel.

The 34 .gc-card tiles do exist, but they start at y = 2787.75 — 2.59 screens below the fold.

They are 166.5 × 249.75 (aspect 0.667), have no eyebrow element (.gc-card-kick /

.gc-kick / .gcbp-kick / .gc-card-eyebrow all return null), child order is

.gcbp-art → .gc-card-ico → .gc-card-b → .gc-card-help, and they wrap instead of bleeding

(last right edge 760 of 1907; 0 sliced).

Bottom bar

| | ours | reference |
|---|---|---|
| box | x 0, y 1019, 1907 × 56, position: absolute; bottom: 0 | y ≈ 1050 |
| background | linear-gradient(rgba(4,7,13,.68), rgba(4,7,13,.95)), border-top: 1px rgba(255,255,255,.07), z-index: 30 | — |
| left | .gcbp-brand ORACLE │ + MENU button, x 14, w 193.44 | STEAM │ MENU |
| right | .gcbp-hints x 1613.36, w 279.64, h 23, 13 px, gap 22 px | OPTIONS / (A) SELECT / (B) BACK |
| legend, keyboard | M MENU · ↵ SELECT · ESC BACK | — |
| legend, gamepad | ≡ MENU · Ⓐ SELECT · Ⓑ BACK | ✅ same convention |
| persistent? | yes — survives .oa-shell-body scrollTop 2850; bottomInset stays 0 | yes |

Missing versus the reference: an OPTIONS entry.

Note the horizontal pan in §1b moves this bar to x = −210 along with everything else.

Background wash (#gcbp-wash)

| property | ours | reference (Deck-Shelves CDP capture of SteamOS 3.8) |
|---|---|---|
| box | x −209.77, y 0, 2326.54 × 1311.5 | — |
| filter | blur(46px) saturate(1.3) | grayscale(1) contrast(1) — opposite sign on chroma |
| transform | scale(1.22) static | 25 s slow zoom, ease 0s 1 alternate |
| opacity | 0.40 | — |
| mask | linear-gradient(#000 0%, rgba(0,0,0,.62) 34%, rgba(0,0,0,0) 72%) | radial 75% 83% at 50% 18%, on two nested wrappers |
| tint layer | ::after = radial-gradient(120% 80% at 50% 0%, var(--gcbp-w), transparent 62%) @ 0.22 | none |
| animation | none | 25 s zoom |
| transition | opacity 0.45s | 0.3 s fade-in |
| children | 0 <img>, 0 <canvas> — inline SVG data-URI on background-image | <img> |

Four measured differences: we saturate where Steam desaturates; our mask is linear

(fades downward only, so colour runs hard into the left and right edges) where Steam's is a

radial ellipse (lets the whole perimeter go black); we add an accent tint Steam does not

have; we have no zoom.

Focus treatment

| | .gcbp-on .gcbp-art | unfocused .gcbp-art |
|---|---|---|
| transform | matrix(1.055, 0, 0, 1.055, 0, 0) — measured 160.36 / 152.00 = 1.0550 | none |
| outline | 2px solid #fff | 1px solid rgba(255,255,255,.1) |
| box-shadow | rgba(0,0,0,.98) 0 22px 46px -18px, rgb(34,217,230) 0 0 30px -6px | rgba(0,0,0,.96) 0 10px 26px -16px |
| transition | transform .17s cubic-bezier(.2,.8,.3,1), box-shadow .17s, outline-color .17s | same |

The treatment is on the art, not the tile — the tile itself measures transform: none,

outline-style: none, box-shadow: none. 1.055 sits inside the cross-platform band

(Android TV 1.025 / 1.05 / 1.1; tvOS 1.05–1.1) and equals Steam's translateZ(15px) at a 288 px

perspective. Focus scale is not a problem.

Two things are missing:

1. Row-level focus. .gpfocuswithin = 0, :focus-within = 0, and

[class*="within"], [class*="row-on"], [class*="shelf-on"] matches 0 elements. Steam has

two states (.gpfocus on one item, .gpfocuswithin on every card in the owning row); we have

one.

2. Real DOM focus. document.activeElement is BODY at every sample, on both viewports. The

model is a CSS class plus a document-level capturing keydown listener. Nothing is

focus-focused, so :focus-visible never fires and there is no accessibility tree signal.

Gamepad

≡ MENU / Ⓐ SELECT / Ⓑ BACK. The (A) SELECT / (B) BACK line is literally true, not decor.

(rAF-paced). It does not poll before a pad connects — correct, no idle cost.

Covers — do they render, and are they stable?

They render.

| game | intrinsic | opaque px | distinct colours (6-bit buckets) | mean luminance |
|---|---|---|---|---|
| Tic-Tac-Toe | 300 × 450 | 100.0 % | 3346 | 69 |
| Connect Four | 300 × 450 | 100.0 % | 2199 | 77 |
| Reversi | 300 × 450 | 100.0 % | 1798 | 76 |
| Gomoku | 300 × 450 | 100.0 % | 1999 | 88 |
| Nim | 300 × 450 | 100.0 % | 2421 | 78 |
| Dots & Boxes | 300 × 450 | 100.0 % | 2119 | 81 |

Each is decoded from a data:image/svg+xml URI (5606–15937 chars, distinct per game) into a

152 × 228 canvas and read back with getImageData over 34,656 pixels.

Pixel differential, clean load, no key input, clip (866, 171, 160 × 241):

They are deterministic. Two independent page loads, 40 covers compared by their full

background-image string: 40 / 40 identical, 0 differing, 0 label mismatches.

---

3. Mobile 375×812

The reference has no phone spec; these are our numbers plus the arithmetic that a

reference-faithful phone layout would require.

| | value |
|---|---|
| status cluster | x 101.89, 136.14 × 40. 🔍 and 📶 collapse to 0 × 0 — 3 visible items |
| .gc-head | 347 × 98 at y 77 (search field + 5 tool buttons + volume slider) = 12.1 % of the screen before any content |
| .gcbp-top | flex-direction: column, gap 12 px |
| hero | 347 × 305.19, aspect 1.14; its art panel is 345 × 108 = 3.19 : 1 |
| first cover top edge | y = 531.19 — 65.4 % of the way down the screen |
| tiles | 108.75 × 163.13, aspect 0.667 |
| row height | 163.13 px = 20.09 vh (reference 34.88 vh → 57.6 %) |
| gap / pitch | 9 px / 117.75 px |
| visible | 3 full + 1 sliced |
| metadata | title 15 px, span 9.5 px, at y 708.31 = 87.2 % down the screen |
| tab strip | justify-content: flex-start; items span 1216.39 px in a 361 px box → scrolls; itemsCentreOffset 434.70 |
| bottom bar | 375 × 54 at y 758; brand w 104.72, hints w 178.16, gap 11 px |
| focus / meta / wash | all still follow arrow keys ✅ |
| shell pan bug | absent ✅ |

The reference row geometry is arithmetically impossible at 375 px. A 34.9 vh row on an 812 px

screen is 283 px tall; a 2.128 : 1 landscape cell at that height is **602 px wide — 1.61× the

entire viewport**. There is no clamp that makes this work; it has to be an explicit decision.

---

4. Page shape — the structural finding

| | desktop 1907×1075 | mobile 375×812 |
|---|---|---|
| scroller | .oa-shell-body (html/body are overflow: hidden, scrollHeight 1075) | same |
| scrollHeight / clientHeight | 3860 / 1010 = 3.82 screens | 4908 / 747 = 6.57 screens |

Big Picture is one screen of rows. Ours is a scrolling page wearing console chrome. Contents:

| y | element | height | tiles |
|---|---|---|---|
| 85 | .gc-head | 44 | — |
| 151 | .gcbp-top (hero + "Start here" rail) | 306 | 14 |
| 481 | .gc-chips.gcbp-tabs | 64 | — |
| 567 | .gc-prog-host | 475.75 | — |
| 1064.75 | .gc-shelf Board & Strategy | 306 | 9 |
| 1392.75 | .gc-shelf Arcade | 306 | 7 |
| 1720.75 | .gc-shelf Puzzle | 306 | 10 |
| 2048.75 | .gc-shelf Word & Trivia | 306 | 2 |
| 2376.75 | .gc-shelf Reflex & Skill | 306 | 6 |
| 2704.75 | .gc-lobby | 61 | — |
| 2787.75 | .gc-shelf.gcbp-gridwrap (All games) | 1121 | 34 cards |

Every shelf heading is 10 px. Every shelf tile is 152 × 228.

The rail also carries mouse affordances Big Picture has none of:

.gc-shelf-nav.gc-shelf-prev at x 864, 44 px wide, and .gc-shelf-next at x 1829, 44 px wide.

When focus scrolls past the top band, the metadata line goes with it —

at .oa-shell-body scrollTop 2850 the bar, status cluster and wash are all still visible, but

.gcbp-meta-on and the hero are off-screen.

---

5. What is already right — do not "fix" these

1. Poster aspect 0.667. aspect-ratio: 2/3 on .gcbp-art, held exactly on all 48 tiles at

both viewports. Matches Valve's 600 × 900 Library Capsule.

2. Left gutter 40 px = 2.098 %. Identical to the reference. TV-platform figures

(Android TV 5–6 %, tvOS 80 pt, Smashing 80 px) would make this worse.

3. Tab strip centring. centreOffset 0.00 and itemsCentreOffset 0.00. Exact.

4. Right-edge bleed on the row. 1 sliced tile, last tile right edge 3179.88 vs viewport 1907.

5. Icons-only status cluster with no container chrome. 0 of 5 items carry a text label.

6. Persistent bottom bar with a glyph+LABEL legend that switches to Ⓐ/Ⓑ on gamepad connect.

7. Focus scale 1.0550, transition .17s cubic-bezier(.2,.8,.3,1).

8. The focus model itself — single focus, metadata follows, background follows, Enter opens.

9. Deterministic drawn covers, 40/40 stable across loads, 300 × 450 intrinsic.

---

6. Gaps, ranked by measured magnitude

| # | gap | measured | reference |
|---|---|---|---|
| 1 | Desktop shell pans 210 px on the first → and stays there | hero x 40 → −170, bar x 0 → −210 | 0 |
| 2 | Row is 39 % too short and 1.47× too dense | 228 px / 21.21 vh, 6.01 across the band | 375 px / 34.88 vh, 4.10 |
| 3 | The screen is 3.82 screens tall | .oa-shell-body 3860 / 1010 | 1.00 |
| 4 | The landscape tile's art is 1.213 : 1 and it carries 104 chars of text | art 368.86 × 304 = 45.9 % of the hero | 2.128 : 1, 0 chars, 100 % |
| 5 | The 44.3 % of the first screen below the tabs is a stats panel | .gc-prog-host 475.75 px | a content row |
| 6 | Wash: saturate instead of desaturate, linear mask instead of radial, extra accent tint, no zoom | blur(46px) saturate(1.3), linear 180°, ::after @ .22, animation: none | grayscale(1) contrast(1), radial 75% 83% at 50% 18%, no tint, 25 s zoom |
| 7 | Section heading is 46 % of size and the wrong kind | 13 px uppercase ls 1.82 px opacity .7 | ~28 px sentence case *(est)* |
| 8 | Title/metadata sit side by side, not stacked | 11 px top delta, span 165.42 px to the right | 27 px below |
| 9 | Focused title 70 % of size | 21 px | ~30 px *(est)* |
| 10 | No row-level focus state | .gpfocuswithin = 0 | .gpfocus + .gpfocuswithin |
| 11 | No real DOM focus | activeElement = BODY at every sample | — |
| 12 | Tab strip carries 10 items, not 3 | span 1305.84 px | 3 pills |
| 13 | Mobile row is 57.6 % of reference and starts 65.4 % down the screen | 163.13 px at y 531.19 | — |
| 14 | Gap 14 px | +40 % | 10 px |
| 15 | Metadata 10.5 px | 88 % | ~12 px *(est)* |
| 16 | Status cluster 788.92 px from the right edge, 5 items | — | right-aligned, 6 |

---

7. Estimates, flagged so they do not harden into facts

These have no published source — Valve publishes no typography or spacing spec for Big

Picture at all:

Everything else in this document is a measured number from a live render, or a Valve-published

capsule ratio.