Files

353 lines
32 KiB
CSS

/* Glass Shell Phase 2 concept "window-nav" (the shell): shared styles for the window-nav-*.html
* mockups and for every area mockup that draws the shell's chrome. Builds on kit.css (DESIGN2
* tokens). Every number is a size or rule from docs/phase2/concepts/window-nav.md; numbers are
* main-window CSS px unless a block says "popup px" (frame.menu, x1.10 in the view) or
* "systemui px" (frame controls, x0.75). Revision 3 (PLAN §1 conformance): state values from
* PLAN §1.4 (focus + .28 with a .16 spot in the upper third; laser hover + .08 and a .12 spot;
* navigation hover = the spot only; navigation selection .18 + arc + Semibold), the ornament's
* quiet legend and gamepad-only glyph badges (§1.10), the 60 / 80 More circle inside its host
* (§1.11), quiet frame controls (VP P-70), text on coloured fills dark (CC §7).
* Session 2 (2026-10-07): the shell's own states (tab bar, ornament members, frame controls, More
* circle) use P4's theme model: + .28 (`--lgs-focus-add`) with normal alpha compositing (P4-D5);
* kit.css still blends with plus-lighter until P4's kit parity (FD-3). C1c's presentation recipe
* (menus, alerts, sheets; REQ C1c->C1a) replaces revision 2's blocks. New: the library's fixed
* ornament slots (data-slots), the quiet legend's dim band on `window` routes (flag quietBacking),
* the page members' recede during alerts and sheets, the More circle without blur inside glass. */
/* ------------------------------------------------------------------ edges (WN §8.2, critique 4)
kit.css draws the E3 specular ring as a conic arc PLUS a full-width linear top layer. On wide
slabs (ornament, Power menu, window) that linear layer became a uniform 2 px line about +100 L
across the whole top: an outline. The shell replaces it with a light-dependent highlight: a
radial lobe centred at 26 % of the width under the key light (above, 20 deg left), fading to
nothing by 78 % of the width and down the sides. With the conic arc this gives "a band near the
top-left plus a faint top edge" (DESIGN2 §6.2 R3) and never a line of constant brightness. */
.lgk-view .lgk-glass::before {
background:
conic-gradient(from var(--lg-light),
rgb(255 255 255 / calc(.62 * var(--rim, 1))) 0deg,
rgb(255 255 255 / calc(.20 * var(--rim, 1))) 38deg,
transparent 72deg, transparent 148deg,
rgb(255 255 255 / calc(.16 * var(--rim, 1))) 180deg,
transparent 214deg, transparent 288deg,
rgb(255 255 255 / calc(.22 * var(--rim, 1))) 322deg,
rgb(255 255 255 / calc(.62 * var(--rim, 1))) 360deg),
radial-gradient(70% 120% at 26% 0%, rgb(255 255 255 / calc(.18 * var(--rim, 1))) 0, rgb(255 255 255 / calc(.06 * var(--rim, 1))) 46%, transparent 75%);
}
/* ------------------------------------------------------------------ state values (PLAN §1.4)
The gamepad focus add is P4's token `--lgs-focus-add` (.28 today, contracts/tokens.md; tuned once in
.28-.32 from G-FOCUS), drawn with normal alpha compositing as the theme does (P4-D5). On the tab
bar's glass (L ~95) .28 measures focus - rest +35 L and focus - current-route circle +7.6 L
(PLAN-1a-2 fails); .32 measures +42.5 and +15.1 (passes). The shell's mockups use .32, the top of
§1.4's range, pending P4's one-time tuning (REQ C1a->P4; window-nav.md §13.1). */
.lgk-view { --wn-focus-add: .32; --wn-focus-spot: .16; }
/* glass tone under text: L 70-90 (PLAN §1.6, D2 §6.3). kit.css aims liquid glass at luma .38 (~97 L) and
its top sheen adds more, so over a bright room the tab bar and the ornament measured L 128-140 (above
the 55-110 range). The shell's liquid glass that carries labels aims at .30 (~77 L), as home-apps.css
does for its cards; glassd's tone mapping does the same on the device. */
.lgk-view :is(.wn-tabcap, .wn-orn.lgk-glass, .wn-framecap) { --lgk-target-luma: .30; }
/* kit.js clamps its adaptation at x0.42, so over the brightest parts of the room (the curtain, L ~235) the
liquid glass still lands near L 110-120. glassd's tone mapping keeps glass L 55-110 and 70-90 under text
(GM v2, PLAN §1.6), and the CSS-only tier is the panel tint (L ~75). The shell's liquid glass with labels
therefore carries a dark tone layer that holds it in the 70-90 band in the mockups. */
.lgk-view:not([data-tier="t1"]) :is(.wn-tabcap, .wn-orn.lgk-glass) {
background: linear-gradient(180deg, rgb(255 255 255 / .10), rgb(255 255 255 / .02) 55%), rgb(16 18 24 / .30); }
/* ------------------------------------------------------------------ window glass */
/* The route's glass mode decides the height (WN §3.1.1, PLAN §1.2): `window` ends at 656 (a
64 px ornament margin: the ornament 628-712 overlaps the glass by 28 and keeps 8 + 19 px clear
of SteamVR's window-bar row); `window-full` (.full) uses the full 720. Never follows focus. */
.wn-win { left: 0; top: 0; width: 1280px; height: 656px; overflow: hidden; }
.wn-win.full { height: 720px; }
.wn-win.dimmed::after {
content: ""; position: absolute; inset: 0; border-radius: inherit; background: rgb(0 0 0 / .35); z-index: 5; pointer-events: none; }
/* scroll edge under the toolbar row: dimming dominates over dark content (DESIGN2 §6.7) */
.wn-edge-top { position: absolute; left: 0; right: 0; top: 0; height: 124px; pointer-events: none; z-index: 2;
background: linear-gradient(180deg, rgb(0 0 0 / .30), rgb(0 0 0 / .12) 55%, transparent); }
.wn-fade-bottom { -webkit-mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 110px), rgb(0 0 0 / .2) calc(100% - 30px), transparent 100%);
mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 110px), rgb(0 0 0 / .2) calc(100% - 30px), transparent 100%); }
/* ------------------------------------------------------------------ toolbar row (#header) */
.wn-toolbar { position: absolute; left: 0; top: 0; width: 1280px; height: 108px; z-index: 4; }
.wn-back { position: absolute !important; left: 24px; top: 24px; }
.wn-back.root { background: transparent; color: var(--lg-text-2); }
.wn-back > .lgk-i { --is: 28px; }
.wn-back.grown { width: auto !important; padding: 0 22px 0 16px; gap: 6px; font: 500 20px/1 var(--lg-font); }
.wn-title { position: absolute; left: 100px; top: 25px; white-space: nowrap; }
.wn-title.centered { left: 0; right: 0; top: 36px; text-align: center; }
.wn-search { position: absolute !important; top: 22px; width: 520px; left: 380px; }
.wn-search.wide { width: 640px; left: 320px; }
.wn-search .typed { flex: 1; font: 500 24px/1 var(--lg-font); color: var(--lg-text-1); white-space: nowrap; }
.wn-search .caret { display: inline-block; width: 2px; height: 28px; margin-left: 2px; vertical-align: -6px; background: #fff; border-radius: 1px; }
.wn-search .clear { --s: 44px; background: rgb(255 255 255 / .16) !important; color: var(--lg-text-1) !important; }
.wn-search .clear .lgk-i { --is: 20px; }
.wn-search.dim, .wn-back.dim, .wn-title.dim { opacity: .45; }
.wn-icon-circle { position: absolute !important; top: 24px; right: 24px; }
/* library routes, laser mode: Steam's Sort & Filter, the toolbar's trailing group (PLAN §1.10, R2-2; C2c):
one 60 px capsule, right edge 24 px inside the glass, >= 20 px clear of the search capsule (x 900) */
.wn-trail { position: absolute; top: 24px; right: 24px; height: 60px; display: flex; align-items: center; gap: 0;
padding: 0 6px; border-radius: 30px; box-sizing: border-box;
background: linear-gradient(180deg, rgb(255 255 255 / .06), rgb(255 255 255 / 0) 60%), rgb(255 255 255 / .10);
box-shadow: inset 0 1.5px 1.5px -1px rgb(255 255 255 / .22), 0 1px 2px rgb(0 0 0 / .10); }
.wn-trail-b { display: inline-flex; align-items: center; gap: 8px; height: 48px; padding: 0 14px; border-radius: 24px;
font: 600 20px/1 var(--lg-font); color: var(--lg-text-1); white-space: nowrap; }
.wn-trail-b .lgk-i { --is: 22px; color: var(--lg-text-2); }
.wn-trail.dim { opacity: .45; }
/* ------------------------------------------------------------------ bottom ornament (#Footer): the ornament contract (WN §3.4, PLAN §1.10)
One capsule, y 628-712, centred, width fit-content <= 960. Members in this order:
[Steam's laser-mode Sort/Filter buttons] [action legends, label first, controller glyph trailing
(gamepad mode only)] [area slot: one segmented control] [nav legends A/B: quiet, in both modes].
Material by content: only A and B present -> the quiet legend (.wn-orn.quiet: no capsule glass,
items keep 60 px and their place). Over 960 px -> compact members (20 px labels, 16 px padding). */
.wn-orn { left: 50%; translate: -50% 0; top: 628px; gap: 4px; padding: 0 12px; max-width: 960px; }
.wn-orn.quiet { position: absolute; display: flex; align-items: center; height: 84px; box-sizing: border-box; z-index: 6; }
.wn-leg { position: relative; display: inline-flex; align-items: center; gap: 12px; height: 60px; padding: 0 14px 0 22px;
border-radius: 999px; font: 600 22px/1 var(--lg-font); color: var(--lg-text-1); white-space: nowrap; }
.wn-leg .lgk-glyph { width: 30px; height: 30px; font: 700 16px/1 var(--lg-font); background: rgb(255 255 255 / .16); color: var(--lg-text-2); }
.wn-leg .tgt { margin-left: -4px; color: var(--lg-text-2); font-weight: 500; max-width: 230px; overflow: hidden; text-overflow: ellipsis; }
.wn-leg.nav { color: var(--lg-text-2); font-weight: 500; }
.wn-orn.mode-laser .wn-leg.nav { padding: 0 22px; } /* labels only: no trailing badge */
.wn-leg.is-disabled { opacity: .38; }
.wn-orn.compact .wn-leg { font-size: 20px; padding: 0 12px 0 16px; gap: 10px; }
.wn-orn.compact .wn-leg.sf, .wn-orn.compact.mode-laser .wn-leg.nav { padding: 0 16px; }
.wn-orn.quiet .wn-leg { padding: 0 22px; }
/* the quiet legend (only A and B): no capsule material. Inside the glass (`window-full`, .in-glass):
labels Medium white .70 (PLAN §1.10). In the ornament margin of a `window` route the labels sit on
the room, where white .70 measures ~1.1 : 1 over a bright room (C1b, C1c), so they sit on the
dim band (flag quietBacking, default "window"; WN §3.4.1, O3): one soft black .62 band, 60 px
tall, its edges blurred, 10 px beyond the members; no rim, no sheen, no backdrop-filter, no slab.
It darkens the room like a shadow; it is not glass. Labels white .82 with the on-room shadow.
.bare = quietBacking "off" (PLAN §1.10's bare labels, kept for comparison). */
.wn-orn.quiet:not(.in-glass):not(.bare)::before { content: ""; position: absolute; left: 2px; right: 2px; top: 12px; bottom: 12px; border-radius: 30px;
background: rgb(0 0 0 / .62); filter: blur(7px); pointer-events: none; z-index: -1; }
.wn-orn.quiet:not(.in-glass):not(.bare) { isolation: isolate; } /* static .wn-orn.quiet markup in area mockups gets the band too */
.wn-orn.quiet:not(.in-glass):not(.bare) .wn-leg { color: rgb(255 255 255 / .82); text-shadow: 0 1px 2px rgb(0 0 0 / .45); }
.wn-orn.quiet.bare:not(.in-glass) .wn-leg { text-shadow: 0 1px 3px rgb(0 0 0 / .55), 0 0 12px rgb(0 0 0 / .35); }
/* fixed slots (the library's 300 · 130 · 156 · 134 · 120 inside an 880 px capsule; shell.ornamentSlots):
uniform 4 px gaps, no group spacers, each member exactly its slot, its content centred; the slot's
hit box takes its 2 px share of each gap (P-07). No name suffix on the Options member. */
.wn-orn.fixed { gap: 4px; padding: 0 12px; max-width: none; }
.wn-orn.fixed .wn-leg { box-sizing: border-box; justify-content: center; gap: 10px; padding: 0 12px; flex: none; }
.wn-orn.fixed .wn-leg .val { overflow: hidden; text-overflow: ellipsis; }
/* while an alert or sheet is open (C1c's html.lgs-modal): the members that act on the page beneath
recede under the tab bar's black .35 layer and stay clickable; A, B and the dialog's legends stay lit */
.wn-orn.modal .wn-leg:not(.nav)::after { opacity: 1; background: rgb(0 0 0 / .35); }
.wn-leg.sf { padding: 0 24px 0 18px; gap: 10px; } /* Steam's %{SortAndFilterButton}, and the T2 Options member */
.wn-leg.sf > .lgk-i { --is: 24px; color: var(--lg-text-2); }
.wn-leg.sf .val { color: var(--lg-text-1); }
.wn-orn .gsep { width: 14px; flex: none; }
/* CSS-only tier (WN §8.4): the page cannot see the room, so the ornament cannot frost it. Over the
window (its top 28 px) the window's own tint is beneath; outside the window: tint .70 + edges. */
.lgk-view[data-tier="t1"] .wn-orn.lgk-glass { -webkit-backdrop-filter: none; backdrop-filter: none;
background: linear-gradient(180deg, rgb(255 255 255 / .06), rgb(255 255 255 / 0) 40%), linear-gradient(180deg, rgb(28 30 40 / .50) 0 28px, rgb(28 30 40 / .70) 28px); }
.wn-leg.is-open { background: var(--lg-fill-selected); color: var(--lg-text-on-selected); }
.wn-leg.is-open .lgk-glyph, .wn-leg.is-open > .lgk-i, .wn-leg.is-open .tgt { background: rgb(13 14 18 / .12); color: rgb(13 14 18 / .7); }
.wn-leg.is-open > .lgk-i { background: transparent; }
.wn-leg.is-open .val { color: var(--lg-text-on-selected); }
.wn-menu-glyph { position: relative; }
.wn-menu-glyph::before { content: ""; width: 13px; height: 2.4px; border-radius: 2px; background: currentColor;
box-shadow: 0 -4.6px 0 currentColor, 0 4.6px 0 currentColor; }
.wn-leg::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
opacity: 0;
background: radial-gradient(circle at var(--hx, 50%) var(--hy, 30%), rgb(255 255 255 / var(--spot, .12)) 0, rgb(255 255 255 / 0) var(--spot-r, 75%)), rgb(255 255 255 / var(--lift, 0)); }
.wn-leg.is-hover::after { opacity: 1; --lift: .08; --spot: .12; }
.wn-leg.is-focus::after { opacity: 1; --lift: var(--wn-focus-add); --spot: var(--wn-focus-spot); --hx: 50% !important; --hy: 28% !important; }
.wn-leg.is-focus::before { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; padding: 1.5px;
background: conic-gradient(from var(--lg-light), rgb(255 255 255 / .8) 0deg, rgb(255 255 255 / .22) 40deg, transparent 75deg, transparent 145deg, rgb(255 255 255 / .2) 180deg, transparent 215deg, transparent 285deg, rgb(255 255 255 / .22) 320deg, rgb(255 255 255 / .8) 360deg);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; }
.wn-orn .lgk-seg { height: 60px; }
.wn-orn .lgk-seg > * { padding: 0 20px; }
/* ------------------------------------------------------------------ tab-bar ornament (frame.menu, popup px; WN §3.3)
Pitch p and circle d come from the live sizing rule (WN §3.3.2). On this device (Console on,
developer mode, r = 1): p 58, d 50, two capsules (6 sections; Console, Steam Settings,
VR Settings, a 10 px section gap, Power). PLAN §1.4: the current route = the navigation
selection (.18 + its specular top arc + Semibold label) drawn as a circle only; laser hover =
the light spot only (navigation rows add no fill); gamepad focus = a pill spanning icon and
label at + .28 with a .16 spot in the upper third and its own arc; the two never share a shape. */
.wn-tabbar { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; --p: 58px; --d: 50px; }
.wn-tabcap { position: relative !important; display: flex; flex-direction: column; padding: 6px 12px; width: calc(var(--d) + 24px);
--r: calc((var(--d) + 24px) / 2); box-sizing: border-box; }
.wn-tabbar.expanded .wn-tabcap { width: 272px; }
.wn-tab { position: relative; display: flex; align-items: center; height: var(--p); color: var(--lg-text-2); flex: none; }
.wn-tab.secgap { margin-top: 10px; }
.wn-tab > .ic { position: relative; z-index: 1; display: grid; place-items: center; flex: none; width: var(--d); height: var(--d); border-radius: 50%; }
.wn-tab > .ic > .lgk-i { --is: calc(var(--d) * .52); }
.wn-tab > .lbl { position: relative; z-index: 1; display: none; margin-left: 10px; font: 500 22px/1 var(--lg-font); color: var(--lg-text-1); white-space: nowrap; }
.wn-tab:is(.is-nav-selected, .is-focus) > .lbl { font-weight: 600; }
.wn-tabbar.expanded .wn-tab > .lbl { display: block; }
.wn-tab .wn-badge { position: absolute; left: calc(var(--d) * .66); top: calc((var(--p) - var(--d)) / 2 + 2px); width: 13px; height: 13px; border-radius: 50%;
background: var(--lg-yellow); box-shadow: 0 0 0 2px rgb(30 30 34 / .5); z-index: 2; }
/* current route: the navigation selection (.18 + its specular top arc) as a circle behind a
heavier white glyph; no label pill */
.wn-tab.is-nav-selected { color: #fff; background: transparent; box-shadow: none; } /* kit's .is-nav-selected fill and arc would paint the whole row: the circle carries them */
.wn-tab.is-nav-selected > .ic { background: rgb(255 255 255 / .18); }
.wn-tab.is-nav-selected > .ic::before { content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; padding: 1.5px;
background: conic-gradient(from var(--lg-light), rgb(255 255 255 / .60) 0deg, rgb(255 255 255 / .16) 40deg, transparent 75deg, transparent 285deg, rgb(255 255 255 / .16) 320deg, rgb(255 255 255 / .60) 360deg);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; }
.wn-tab.is-nav-selected > .ic > .lgk-i { stroke-width: 2.8; }
/* laser hover (navigation row: the light spot only, no fill) and gamepad focus (always the expanded pill) */
.wn-tab::after { content: ""; position: absolute; left: 0; right: 0; top: calc((var(--p) - var(--d)) / 2); height: var(--d); border-radius: 999px;
pointer-events: none; opacity: 0;
background: radial-gradient(circle at var(--hx, 22%) var(--hy, 30%), rgb(255 255 255 / var(--spot, .12)) 0, transparent var(--spot-r, 70%)), rgb(255 255 255 / var(--lift, 0)); }
.wn-tabbar:not(.expanded) .wn-tab::after { right: auto; width: var(--d); }
.wn-tab.is-hover { color: var(--lg-text-1); }
.wn-tab.is-hover::after { opacity: 1; --lift: 0; --spot: .12; }
.wn-tabbar:not(.expanded) .wn-tab.is-hover::after { --hx: 50%; --hy: 40%; }
.wn-tab.is-focus { color: #fff; }
.wn-tab.is-focus::after { opacity: 1; --lift: var(--wn-focus-add); --spot: var(--wn-focus-spot); --hx: 50%; --hy: 26%; --spot-r: 85%; }
/* white labels on a bright focus fill: a soft shadow keeps them legible (contrast note in window-nav.md §13.1) */
.wn-tab.is-focus > .lbl, .wn-leg.is-focus { text-shadow: 0 1px 2px rgb(0 0 0 / .35); }
.wn-tab.is-focus::before { content: ""; position: absolute; left: 0; right: 0; top: calc((var(--p) - var(--d)) / 2); height: var(--d); border-radius: 999px; pointer-events: none; padding: 1.5px; z-index: 2;
background: conic-gradient(from var(--lg-light), rgb(255 255 255 / .85) 0deg, rgb(255 255 255 / .22) 40deg, transparent 75deg, transparent 145deg, rgb(255 255 255 / .22) 180deg, transparent 215deg, transparent 285deg, rgb(255 255 255 / .24) 320deg, rgb(255 255 255 / .85) 360deg);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; }
.wn-tabbar:not(.expanded) .wn-tab.is-focus::before { right: auto; width: var(--d); }
/* the source of an open menu is white (Power while the Power menu shows) */
.wn-tab.is-open > .ic { background: var(--lg-fill-selected); color: var(--lg-text-on-selected); }
/* a receding ornament darkens (scene-graph tint / CSS scrim); it never fades to transparent */
.wn-dimmed::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: rgb(0 0 0 / .35); z-index: 4; pointer-events: none; }
/* ------------------------------------------------------------------ SteamVR window-bar row (systemui px; WN §3.5)
The frame controls (keyboard, float, theater, more [, close]) in one panel-glass capsule, sitting
left of the visionOS window-bar pill (SteamVR's grab handle moved up from under the dashboard bar):
one row directly under the window instead of two separate strata. */
.wn-frame { display: flex; align-items: center; gap: 14px; }
.wn-framecap { position: relative !important; display: flex; align-items: center; gap: 27px; height: 100px; padding: 0 10px; --r: 50px; box-sizing: border-box; }
/* circles rest quiet (fill .06, glyph .70; with SteamVR's idle dim .6 the glyph reads at .42, VP P-70)
and become full circles on hover (.14 = rest + .08, and the spot) or gamepad focus (+ .28, arc) */
.wn-framecap .lgk-btn { --s: 80px; background: rgb(255 255 255 / .06); color: var(--lg-text-2); }
.wn-framecap .lgk-btn.is-hover { background: rgb(255 255 255 / .14); color: #fff; }
.wn-framecap .lgk-btn.is-focus { color: #fff; }
.wn-framecap .lgk-btn::after { mix-blend-mode: normal; }
.wn-framecap .lgk-btn .lgk-i { --is: 36px; }
.wn-framecap .sep { width: 10px; }
.wn-framecap .lgk-btn.danger:is(.is-focus, .is-hover) { background: var(--lg-tint-danger) !important; color: #fff; }
.wn-framecap .lgk-btn.is-hover::after { --lift: 0; --spot: .12; }
.wn-framecap .lgk-btn.is-focus::after { --lift: var(--wn-focus-add); --spot: var(--wn-focus-spot); --hx: 50%; --hy: 28%; }
.wn-tip { position: absolute; white-space: nowrap; padding: 0 30px; height: 60px; display: flex; align-items: center;
font: 600 30px/1 var(--lg-font); --r: 30px; }
/* the visionOS window bar = SteamVR's grab handle: 467 x 32 grab px (140 x 9.6 mm) -> 183 x 12.5 view px */
.wn-pill { position: absolute; width: 183px; height: 12.5px; border-radius: 7px; background: rgb(255 255 255 / .46);
box-shadow: 0 1px 4px rgb(0 0 0 / .28), inset 0 1px 1px rgb(255 255 255 / .5); }
.wn-pill.hover { background: rgb(255 255 255 / .92); width: 198px; }
/* ------------------------------------------------------------------ dashboard bar (bar px, x1.20; system concept owns its content) */
.wn-bar { display: flex; gap: 16px; }
.wn-barcap { position: relative !important; height: 80px; gap: 8px; padding: 0 8px; --r: 40px; }
.wn-barcap .lgk-btn { --s: 64px; }
/* ------------------------------------------------------------------ the pointer proxy (T2, WN §3.6; native mode only, PLAN §1.11)
Drawn by the shell inside Steam's own DOM at the laser's hit point, so it is part of the texture
that every base crop and popped crop shows, whatever SteamVR does with its own dot. In CSS-only
mode it is not drawn (SteamVR's own dot shows): T1 mockups omit it. */
/* a 16 px ring with a 6 px core: where SteamVR's own dot also shows, the ring reads as its halo */
.wn-proxy { position: absolute; width: 16px; height: 16px; margin: -8px 0 0 -8px; border-radius: 50%; box-sizing: border-box; z-index: 52; pointer-events: none;
border: 2.5px solid rgb(255 255 255 / .95);
background: radial-gradient(circle, #fff 0 3px, transparent 3.5px);
box-shadow: 0 0 0 1.5px rgb(0 0 0 / .30), inset 0 0 0 1px rgb(0 0 0 / .18), 0 0 14px 4px rgb(255 255 255 / .40); }
/* ------------------------------------------------------------------ the item "More" circle (T2 helper owned by the shell, WN §3.4.4, PLAN §1.11)
60 px visible inside its host (cards: top right, inset 10; rows: trailing, inset 24) with an
80 px hit (transparent padding, not drawn); the clear material over content: black .38 + 10 px
blur; white .94 with a dark glyph while its menu is open. It rides its host's depth. */
.wn-more { position: absolute; width: 60px; height: 60px; display: grid; place-items: center; border-radius: 50%; z-index: 6; box-sizing: border-box;
color: #fff; background: rgb(0 0 0 / .38); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
box-shadow: inset 0 1.5px 1.5px -1px rgb(255 255 255 / .22), 0 2px 8px rgb(0 0 0 / .22); }
.wn-more .lgk-i { --is: 26px; }
.wn-more::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0;
background: radial-gradient(circle at var(--hx, 40%) var(--hy, 35%), rgb(255 255 255 / .12) 0, transparent 75%), rgb(255 255 255 / .08); }
.wn-more.is-hover::after { opacity: 1; }
.wn-more.is-open { background: var(--lg-fill-selected) !important; color: var(--lg-text-on-selected); -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
/* inside another glass container (the search sheet, a sheet): the fill only, no blur (no glass in glass, VP P-45) */
.wn-more.in-glass { -webkit-backdrop-filter: none; backdrop-filter: none; }
/* ------------------------------------------------------------------ menus, alerts and sheets: C1c's presentation recipe
(WN §5.1-§5.4, PLAN §1.4, §1.7, §1.8, §1.12; the `c1c-recipe` block of window-nav-menu.html, adopted here on
REQ C1c->C1a so area mockups that draw .wn-menu / .wn-alert / .wn-sheet get the same values). C1c owns the look;
change it there first. Focus on a white or coloured fill uses P4's --lgs-white-glow value (contracts/tokens.md). */
.lgk-view { --wn-dark: #0d0e12; --wn-red: rgb(255 66 69 / .92); --wn-white-glow: 0 0 22px 8px rgb(255 255 255 / .55); }
/* slab and rows (E-MENU) */
.wn-menu { padding: 8px; display: flex; flex-direction: column; gap: 6px; box-sizing: border-box; --r: 32px; }
.wn-menu .lgk-row { height: 72px; border-radius: 24px; padding: 0 22px 0 16px; gap: 16px; font: 500 24px/1.2 var(--lg-font); flex: none; }
.wn-menu.compact { gap: 4px; }
.wn-menu.compact .lgk-row { height: 60px; border-radius: 22px; } /* 60 visible on a 64 px pitch */
.wn-menu .lgk-row .lead { width: 28px; display: grid; place-items: center; --is: 26px; flex: none; color: var(--lg-text-1); } /* symbol (actions) or check (values) */
.wn-menu .lgk-row .lead.check .lgk-i { --is: 24px; stroke-width: 3; }
.wn-menu .lgk-row .trail .lgk-i { --is: 22px; }
.wn-menu .sep, .wn-menu .gap { height: 6px; flex: none; } /* Steam's separator = space, no line */
.wn-menu .hdr { display: flex; align-items: center; box-sizing: border-box; height: 40px; padding: 4px 20px 0; font: 700 22px/1 var(--lg-font); color: var(--lg-text-2); flex: none; }
.wn-menu .ihdr { display: flex; align-items: flex-end; box-sizing: border-box; height: 26px; padding: 0 0 4px 18px; font: 600 19px/1 var(--lg-font); color: var(--lg-text-3); flex: none; }
/* legacy: a trailing check (revision 2). C1c's rule is the leading .lead.check; kept only so area mockups not yet moved still render */
.wn-menu .lgk-row .chk { margin-left: auto; --is: 24px; color: var(--lg-text-1); }
/* Steam's Cancel item: a 56 px quiet capsule (PLAN §1.12, HA-11) */
.wn-menu .cancel { align-self: center; flex: none; --s: 56px; padding: 0 44px; margin-top: 6px; background: rgb(255 255 255 / .08);
color: var(--lg-text-2); font: 500 22px/1 var(--lg-font); }
.wn-menu .cols2 { display: grid; grid-template-columns: 1fr 1fr; grid-auto-flow: column; gap: 6px 8px; }
.wn-menu .cols2 .lgk-row { min-width: 0; }
.wn-menu .cols2 .lgk-row .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* states (PLAN §1.4): laser hover = + .08 and a .12 spot at the pointer, no arc; gamepad focus = + .28 uniform,
a static .16 spot in the upper third, the control's own arc x1.5 */
:is(.wn-menu, .wn-alert, .wn-sheet) :is(.lgk-row, .lgk-btn).is-hover::after { opacity: 1; --lift: .08; --spot: .12; }
:is(.wn-menu, .wn-alert, .wn-sheet) :is(.lgk-row, .lgk-btn).is-focus::after { opacity: 1; --lift: .28; --spot: .16; --hx: 50%; --hy: 16%; }
:is(.wn-menu, .wn-alert, .wn-sheet) :is(.lgk-row, .lgk-btn).is-focus::before {
background: conic-gradient(from var(--lg-light), rgb(255 255 255 / .9) 0deg, rgb(255 255 255 / .3) 40deg, transparent 75deg, transparent 145deg,
rgb(255 255 255 / .3) 180deg, transparent 215deg, transparent 285deg, rgb(255 255 255 / .3) 320deg, rgb(255 255 255 / .9) 360deg); }
/* tones (WN §5.1.2 + CC §7: text on a coloured fill is dark; white only on blue) */
.wn-menu .lgk-row.primary, .wn-menu .lgk-row.primary.play { background: linear-gradient(180deg, rgb(255 255 255 / .16), rgb(255 255 255 / 0) 60%), var(--lg-tint-play); color: var(--wn-dark); font-weight: 600; }
.wn-menu .lgk-row.primary.install { background: linear-gradient(180deg, rgb(255 255 255 / .16), rgb(255 255 255 / 0) 60%), var(--lg-tint-primary); color: #fff; font-weight: 500; }
.wn-menu .lgk-row.primary .lead { color: var(--wn-dark); }
.wn-menu .lgk-row.primary.install .lead { color: #fff; }
.wn-menu .lgk-row.danger { color: var(--lg-red); font-weight: 600; } /* <= 2 destructive rows: red label */
.wn-menu .lgk-row:is(.danger, .danger-glyph) .lead { color: var(--lg-red); } /* > 2 (Power): red glyph, white label */
.wn-menu .lgk-row:is(.danger, .danger-glyph).is-focus { background: linear-gradient(180deg, rgb(255 255 255 / .14), rgb(255 255 255 / 0) 60%), var(--wn-red);
color: var(--wn-dark); font-weight: 600; }
.wn-menu .lgk-row:is(.danger, .danger-glyph).is-focus .lead { color: var(--wn-dark); }
.wn-menu .lgk-row:is(.danger, .danger-glyph, .primary).is-focus::after { --lift: .04; --spot: .10; }
/* focus on a white or coloured fill: a blurred outer glow, never a ring (P-16) */
:is(.wn-menu, .wn-alert, .wn-sheet) .is-focus:is(.primary, .danger, .danger-glyph, .is-selected, .red) { box-shadow: var(--wn-white-glow); }
/* source card while its menu is open (PLAN §1.7): 0 mm with a CSS glow, unless it was the gamepad-focused card */
.lgk-view .src-glow { box-shadow: 0 10px 28px rgb(0 0 0 / .42), 0 0 26px 3px rgb(255 255 255 / .20) !important; }
/* alerts and sheets (WN §5.3, §5.4; PLAN §1.12): alerts 640 wide, centred on the glass; sheets <= 960 */
.wn-alert { width: 640px; padding: 36px 36px 28px; --r: 44px; box-sizing: border-box; }
.wn-alert .t { font: 700 28px/1.25 var(--lg-font); letter-spacing: -.01em; }
.wn-alert .b { margin-top: 12px; font: 500 22px/1.36 var(--lg-font); color: var(--lg-text-2); }
.wn-alert .btns { display: flex; gap: 16px; margin-top: 30px; }
.wn-alert .btns .lgk-btn { flex: 1; --s: 60px; }
:is(.wn-alert, .wn-sheet) .lgk-btn.red { background: linear-gradient(180deg, rgb(255 255 255 / .16), rgb(255 255 255 / 0) 55%), var(--wn-red); color: var(--wn-dark); }
.wn-sheet { --r: 44px; box-sizing: border-box; }
/* ------------------------------------------------------------------ search sheet (WN §4) */
.wn-snap { position: absolute; inset: 0; pointer-events: none; } /* the context snapshot: inert decorative clone */
.wn-ssheet { --r: 44px; box-sizing: border-box; }
.wn-ssheet .sh { position: absolute; left: 40px; font: 700 26px/1 var(--lg-font); letter-spacing: -.01em; }
/* ------------------------------------------------------------------ keyboard (Steam VR keyboard, keyboard px x0.74) */
.wn-kb { position: relative !important; width: 854px; padding: 8px; --r: 30px; box-sizing: border-box; }
.wn-kb .echo { display: flex; align-items: center; gap: 12px; height: 46px; margin: 0 4px 6px; padding: 0 22px; border-radius: 26px;
background: rgb(0 0 0 / .30); box-shadow: inset 0 2px 5px rgb(0 0 0 / .26); font: 500 26px/1 var(--lg-font); }
.wn-kb .echo .lbl { color: var(--lg-text-2); font-size: 22px; font-weight: 600; }
.wn-kb .echo .ph { color: rgb(255 255 255 / .5); }
.wn-kb .row { display: flex; gap: 6px; margin-top: 6px; }
.wn-kb .k { flex: 1; height: 42px; border-radius: 11px; background: rgb(255 255 255 / .13); display: grid; place-items: center;
font: 500 21px/1 var(--lg-font); color: var(--lg-text-1); }
.wn-kb .k.w15 { flex: 1.5; } .wn-kb .k.w2 { flex: 2; } .wn-kb .k.w6 { flex: 6.4; }
.wn-kb .k.dark { background: rgb(255 255 255 / .06); color: var(--lg-text-2); font-size: 18px; font-weight: 600; }
.wn-kb .k.enter { background: var(--lg-tint-primary); color: #fff; font-size: 18px; font-weight: 700; }
.wn-kb .k.hot { background: #fff; color: #0d0e12; }
/* ------------------------------------------------------------------ toast (notifications quad, popup px) */
.wn-toast { position: relative !important; width: 320px; height: 76px; --r: 30px; display: flex; align-items: center; gap: 14px; padding: 0 18px 0 14px; box-sizing: border-box; }
.wn-toast .ic { width: 48px; height: 48px; border-radius: 50%; flex: none; display: grid; place-items: center; color: #fff; --is: 26px;
box-shadow: inset 0 1px 1px rgb(255 255 255 / .35); }
.wn-toast .tt { font: 600 20px/1.3 var(--lg-font); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wn-toast .bd { font: 500 18px/1.3 var(--lg-font); color: var(--lg-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ------------------------------------------------------------------ figure captions (outside the scene) */
.wn-cap { position: absolute; z-index: 60; font: 600 18px/1.3 var(--lg-font); color: #fff; padding: 8px 14px; border-radius: 12px;
background: rgb(0 0 0 / .55); }
.wn-cap b { font-weight: 700; }
.wn-cap .m { font-weight: 500; color: rgb(255 255 255 / .75); }