PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 All 37 releases
desktop-mode / assets / css / window-states.css

window-states.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.13, at assets/css/window-states.css

697 lines 21.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * OpenStation — Window state visuals.
3 *
4 * Every rule that keys off a `--state` modifier class: the resize
5 * handle + its overlay shield (drag/resize iframe-interception),
6 * maximize, fullscreen, the opening animation, minimize + closing
7 * fade-outs, reduced-motion scoped overrides, and the RTL cursor
8 * flip on the resize corner.
9 *
10 * Base chrome lives in window-chrome.css; the zoom-out grid lives
11 * in window-overview.css.
12 *
13 * @since 6.9.0
14 */
15
16 /*
17 * Resize handles — one per corner (NE / NW / SE / SW). Each is an
18 * invisible square hit-target whose cursor telegraphs the resize
19 * axis. SE keeps the legacy grip-lines glyph so the affordance is
20 * visible even without hover; the other three remain transparent
21 * (discoverable by cursor change only) to keep the window chrome
22 * clean.
23 *
24 * Hit-area is `--os-resize-size` (20 px by default). The
25 * handles extend a few pixels OUTSIDE the window edge so a cursor
26 * landing just past the border still activates them — matches how
27 * native OS windows forgive edge-grabs.
28 */
29 .os-window__resize-handle {
30 position: absolute;
31 width: var(--os-resize-size);
32 height: var(--os-resize-size);
33 z-index: 999;
34 }
35
36 /*
37 * Desktop-theme corner ornaments (WINDOW_CORNER_{NE,NW,SE,SW}
38 * texture slots).
39 *
40 * ## Why they are INSET rather than centred on the handle
41 *
42 * The obvious placement — centre the ornament on the resize handle —
43 * is wrong, and looks it. `.os-window` sets
44 * `overflow: hidden` (it clips iframe content to the rounded
45 * corners), and the handles deliberately sit 3px OUTSIDE the window
46 * box so an edge-grab still lands. So a handle-centred ornament gets
47 * clipped twice: the part past the window edge is cut, and then
48 * `border-radius` shaves what remains. The result reads as if the
49 * artwork were painted behind the window.
50 *
51 * Escaping the clip is not an option worth its cost: the ornaments
52 * would have to leave `.os-window` entirely — a wrapper
53 * element, or an overlay in `.os-area` whose geometry is
54 * re-tracked on every drag/resize frame. A theme that genuinely
55 * wants artwork OUTSIDE the frame already has the right tool in the
56 * `WINDOW_FRAME` slot: `border-image` paints in the border area,
57 * which `overflow` does not clip.
58 *
59 * So the ornament is anchored inside the window's rounded corner
60 * instead. The default inset clears the handle's 3px overhang plus a
61 * share of the corner radius, which is what keeps the arc from
62 * cutting it at larger radii. Themes that want it tighter or looser
63 * override `--os-window-corner-inset`.
64 *
65 * Physical `top`/`left`/`right`/`bottom` rather than logical
66 * properties here, deliberately: these track the resize handles,
67 * which are physical corners by nature (the NW handle is top-left in
68 * both LTR and RTL) and are themselves positioned physically.
69 *
70 * With no theme active every `--os-window-corner-*-image`
71 * is unset, the shorthand resolves to `none`, and the pseudo-element
72 * paints nothing — zero visual change and no image request.
73 *
74 * NOTE the BEM modifiers are `--ne` / `--nw` / `--se` / `--sw` on
75 * `.os-window__resize-handle`, not bare `.--ne`.
76 */
77 .os-window__resize-handle--ne::after,
78 .os-window__resize-handle--nw::after,
79 .os-window__resize-handle--se::after,
80 .os-window__resize-handle--sw::after {
81 content: "";
82 position: absolute;
83 /* 3px = the handle's overhang past the window edge (see the
84 * `--nw` / `--ne` / `--sw` / `--se` rules above). The radius
85 * share keeps the ornament clear of the corner arc. */
86 --_dm-corner-inset: var(
87 --os-window-corner-inset,
88 calc( 3px + var( --os-window-radius, 8px ) * 0.4 )
89 );
90 width: var( --os-window-corner-size, 16px );
91 height: var( --os-window-corner-size, 16px );
92 /* Ornaments are decoration; the handle keeps the drag affordance. */
93 pointer-events: none;
94 background-repeat: no-repeat;
95 background-position: center;
96 background-size: contain;
97 }
98
99 .os-window__resize-handle--nw::after {
100 top: var( --_dm-corner-inset );
101 left: var( --_dm-corner-inset );
102 background-image: var( --os-window-corner-nw-image, none );
103 }
104
105 .os-window__resize-handle--ne::after {
106 top: var( --_dm-corner-inset );
107 right: var( --_dm-corner-inset );
108 background-image: var( --os-window-corner-ne-image, none );
109 }
110
111 .os-window__resize-handle--sw::after {
112 bottom: var( --_dm-corner-inset );
113 left: var( --_dm-corner-inset );
114 background-image: var( --os-window-corner-sw-image, none );
115 }
116
117 .os-window__resize-handle--se::after {
118 bottom: var( --_dm-corner-inset );
119 right: var( --_dm-corner-inset );
120 background-image: var( --os-window-corner-se-image, none );
121 }
122
123 .os-window__resize-handle--se {
124 right: -3px;
125 bottom: -3px;
126 cursor: nwse-resize;
127 background: linear-gradient(
128 135deg,
129 transparent 30%,
130 var(--os-resize-color) 30%,
131 var(--os-resize-color) 40%,
132 transparent 40%,
133 transparent 55%,
134 var(--os-resize-color) 55%,
135 var(--os-resize-color) 65%,
136 transparent 65%,
137 transparent 80%,
138 var(--os-resize-color) 80%
139 );
140 }
141
142 .os-window__resize-handle--sw {
143 left: -3px;
144 bottom: -3px;
145 cursor: nesw-resize;
146 }
147
148 .os-window__resize-handle--ne {
149 right: -3px;
150 top: -3px;
151 cursor: nesw-resize;
152 }
153
154 .os-window__resize-handle--nw {
155 left: -3px;
156 top: -3px;
157 cursor: nwse-resize;
158 }
159
160 /*
161 * The top corners sit over the title bar, above its controls. A theme
162 * whose title bar puts a control in a corner (a close button flush
163 * with the frame) shrinks them so that control stays clickable; the
164 * bottom corners keep their size.
165 */
166 .os-window__resize-handle--ne,
167 .os-window__resize-handle--nw {
168 width: var(--os-resize-top-size, var(--os-resize-size));
169 height: var(--os-resize-top-size, var(--os-resize-size));
170 }
171
172 /*
173 * During drag/resize, overlay the iframe to prevent it from
174 * capturing pointer events and breaking the drag.
175 */
176 .os-window--dragging .os-window__body::after,
177 .os-window--resizing .os-window__body::after {
178 content: "";
179 position: absolute;
180 inset: 0;
181 z-index: 10;
182 }
183
184 /*
185 * Maximized state: the pixel geometry is applied inline from JS
186 * (matching the desktop area) so left/top/width/height can animate.
187 * Only cosmetic overrides here.
188 */
189 .os-window--maximized {
190 border-radius: 0;
191 }
192
193 .os-window--maximized .os-window__resize-handle {
194 display: none;
195 }
196
197 /*
198 * Fullscreen ("focus") state — the window escapes the desktop area and
199 * covers the entire viewport, hiding the admin bar and dock behind
200 * it. Uses position: fixed so no ancestor overflow clips it, and
201 * a z-index above the admin bar (--os-z-adminbar). Inline
202 * geometry set by toggleMaximize() is overridden with !important because
203 * we never want fullscreen to respect a saved x/y/width/height.
204 */
205 .os-window--fullscreen {
206 position: fixed !important;
207 left: 0 !important;
208 top: 0 !important;
209 width: 100vw !important;
210 height: 100vh !important;
211 z-index: var(--os-z-fullscreen) !important;
212 border-radius: 0 !important;
213 border: none;
214 }
215
216 .os-window--fullscreen .os-window__resize-handle {
217 display: none;
218 }
219
220 /*
221 * Opening animation — subtle scale-up + fade-in.
222 * The class is added on creation and removed after the animation ends.
223 */
224 @keyframes os-window-open {
225 from {
226 opacity: 0;
227 transform: scale(0.92);
228 }
229 to {
230 opacity: 1;
231 transform: scale(1);
232 }
233 }
234
235 .os-window--opening {
236 animation: os-window-open 0.2s ease forwards;
237 }
238
239 /*
240 * Shake animation — used by `Window.shake()` for nudge / attention-
241 * grab affordances. The window briefly jiggles horizontally while
242 * keeping its position. Composes via CSS variable so the inline
243 * `left`/`top` written by the window manager keeps wining; the
244 * shake is a translate ON TOP of that.
245 *
246 * Reduced-motion fallback: a static accent ring for the same
247 * duration so users with prefers-reduced-motion still see the
248 * affordance without movement.
249 */
250 @keyframes os-window-shake {
251 0%, 100% { transform: translateX( 0 ); }
252 8% { transform: translateX( -10px ); }
253 18% { transform: translateX( 9px ); }
254 28% { transform: translateX( -8px ); }
255 38% { transform: translateX( 7px ); }
256 48% { transform: translateX( -5px ); }
257 60% { transform: translateX( 4px ); }
258 72% { transform: translateX( -3px ); }
259 84% { transform: translateX( 2px ); }
260 }
261
262 .os-window--shaking {
263 animation: os-window-shake 0.6s cubic-bezier( 0.36, 0.07, 0.19, 0.97 ) both;
264 }
265
266 @media ( prefers-reduced-motion: reduce ) {
267 .os-window--shaking {
268 animation: none;
269 outline: 3px solid var( --wp-admin-theme-color, #2271b1 );
270 outline-offset: 2px;
271 transition: outline 0.6s ease;
272 }
273 }
274
275 /*
276 * Desktop-switch slide — applied to `.os-area` (the windows
277 * + widgets container) when the user moves between virtual desktops
278 * via arrow keys or any other directional caller. The wallpaper lives
279 * OUTSIDE this element (sibling under `.os-shell`), so the
280 * slide reveals the wallpaper as a backdrop — no black flash, no
281 * awkward gap on the leading edge.
282 *
283 * `--sliding-from-right` plays when moving to the next desktop (the
284 * rightward arrow press): the new desktop's content rushes in from
285 * the right and settles to centre. `--sliding-from-left` mirrors it
286 * for the previous-desktop direction. Wrap-around presses (rightmost
287 * → first via ArrowRight) keep the direction the user perceived,
288 * not the index delta.
289 *
290 * Opacity tapers from 0.35 → 1 so the moving content visibly arrives
291 * rather than just sliding in at full strength, but the wallpaper
292 * stays at full opacity behind it for an anchored sense of place.
293 * Cubic-bezier ease-out lands cleanly without overshoot.
294 *
295 * `forwards` isn't needed — the JS strips the class on `animationend`
296 * so the element returns to its natural transform / opacity state.
297 */
298 @keyframes os-area-slide-from-right {
299 from {
300 transform: translateX(64px);
301 opacity: 0.35;
302 }
303 to {
304 transform: translateX(0);
305 opacity: 1;
306 }
307 }
308
309 @keyframes os-area-slide-from-left {
310 from {
311 transform: translateX(-64px);
312 opacity: 0.35;
313 }
314 to {
315 transform: translateX(0);
316 opacity: 1;
317 }
318 }
319
320 .os-area--sliding-from-right {
321 animation: os-area-slide-from-right 0.28s cubic-bezier( 0.2, 0.7, 0.2, 1 );
322 }
323
324 .os-area--sliding-from-left {
325 animation: os-area-slide-from-left 0.28s cubic-bezier( 0.2, 0.7, 0.2, 1 );
326 }
327
328 @media ( prefers-reduced-motion: reduce ) {
329 .os-area--sliding-from-right,
330 .os-area--sliding-from-left {
331 animation: none;
332 }
333 }
334
335 /*
336 * Desktop-name HUD. `pointer-events: none` is required, not cosmetic:
337 * it sits over the wallpaper's click surface, which minimizes every
338 * window.
339 */
340 .os-desktop-name-hud {
341 position: absolute;
342 top: 50%;
343 left: 50%;
344 transform: translate( -50%, -50% );
345 z-index: var( --os-z-desktop-name, 195 );
346 max-width: min( 70%, 520px );
347 padding: 14px 28px;
348 border-radius: 16px;
349 font-size: 20px;
350 font-weight: 600;
351 letter-spacing: 0.01em;
352 text-align: center;
353 white-space: nowrap;
354 overflow: hidden;
355 text-overflow: ellipsis;
356 color: var( --os-ui-fg-on-accent, #fff );
357 background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.32 ) );
358 backdrop-filter: blur( 18px ) saturate( 140% );
359 -webkit-backdrop-filter: blur( 18px ) saturate( 140% );
360 box-shadow:
361 0 8px 28px rgba( 0, 0, 0, 0.35 ),
362 inset 0 0 0 1px rgba( 255, 255, 255, 0.08 );
363 pointer-events: none;
364 opacity: 1;
365 transition: opacity 0.26s ease;
366 }
367
368 .os-desktop-name-hud--out {
369 opacity: 0;
370 }
371
372 @media ( prefers-reduced-motion: reduce ) {
373 .os-desktop-name-hud {
374 transition: none;
375 }
376 }
377
378 /*
379 * Minimized state — window is hidden and non-interactive at the dock.
380 * Uses opacity + pointer-events + transition: none so neither state
381 * changes nor WAAPI animation teardown trigger accidental transitions.
382 */
383 .os-window--minimized {
384 opacity: 0;
385 pointer-events: none;
386 transition: none !important;
387 }
388
389 /*
390 * Genie helpers — active only during the WAAPI flight to/from the
391 * dock. Disable the base `left/top/width/height/transform/opacity`
392 * transition so the CSS transition doesn't fight the WAAPI
393 * `element.animate()` that drives the scale-to-dock motion. The
394 * minimized class is applied AFTER the minimizing flight lands, and
395 * the restoring class is removed AFTER the restoring flight lands.
396 */
397 .os-window--minimizing,
398 .os-window--restoring {
399 transition: none !important;
400 }
401
402 /*
403 * `pointer-events: none` on the window root above is not enough on
404 * its own: a number of descendants (most notably `.os-
405 * file-tile`, but also other tile renderers) explicitly set
406 * `pointer-events: auto` so the wallpaper / folder layer doesn't
407 * eat their clicks. That `auto` overrides the parent's `none`, and
408 * after "Show Desktop" minimizes every window, hovering the empty
409 * space where a tile used to sit would still trigger its tooltip
410 * / dock peek — the invisible-but-still-interactive bug.
411 *
412 * The universal selector is scoped INSIDE the minimized window so
413 * it doesn't touch anything else, and `!important` is required to
414 * win over the per-tile `pointer-events: auto`.
415 */
416 .os-window--minimized *,
417 .os-window--closing * {
418 pointer-events: none !important;
419 }
420
421 /*
422 * Closing animation — fade out + slight scale down.
423 * The JS waits for transitionend then removes the element.
424 */
425 .os-window--closing {
426 opacity: 0;
427 transform: scale(0.95);
428 pointer-events: none;
429 will-change: transform, opacity;
430 }
431
432 /*
433 * Respect prefers-reduced-motion: collapse all durations to near-zero
434 * so state changes are instant but still technically transition
435 * (avoids layout jumps from display: none).
436 */
437 @media ( prefers-reduced-motion: reduce ) {
438 .os-window {
439 transition-duration: 0.01ms !important;
440 }
441 .os-window--opening {
442 animation-duration: 0.01ms !important;
443 }
444 .os-window--minimizing,
445 .os-window--restoring {
446 animation: none !important;
447 transition: none !important;
448 }
449 }
450
451 /*
452 * RTL note: resize handles use physical `left` / `right` anchors
453 * (not logical `inset-inline-*`) so the four corners stay in the
454 * same physical positions under RTL. Each handle already has the
455 * correct cursor for its physical diagonal (SE + NW use
456 * `nwse-resize`, NE + SW use `nesw-resize`); no RTL override
457 * needed.
458 */
459
460 /* ---------------------------------------------------------------
461 * Snap zones — edge-snap preview rectangle + snapped-state chrome.
462 *
463 * Preview appears while the drag cursor is within SNAP_EDGE_THRESHOLD
464 * of the left/right edge (see window-manager/snap-zones.ts). It's a
465 * translucent accent-colored rectangle covering the target half, with
466 * a bright border so the user sees exactly where the window will land.
467 * --------------------------------------------------------------- */
468 .os-snap-preview {
469 position: absolute;
470 background: color-mix(
471 in srgb,
472 var( --wp-admin-theme-color, #2271b1 ) 14%,
473 transparent
474 );
475 border: 2px solid var( --wp-admin-theme-color, #2271b1 );
476 border-radius: 12px;
477 box-shadow:
478 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
479 0 12px 36px rgba( 0, 0, 0, 0.22 );
480 pointer-events: none;
481 z-index: 99; /* Just under windows (which start at 100) so a */
482 /* still-dragging window renders on top. */
483 opacity: 0;
484 transition:
485 opacity 0.15s ease,
486 left 0.18s cubic-bezier( 0.2, 0, 0.2, 1 ),
487 top 0.18s cubic-bezier( 0.2, 0, 0.2, 1 ),
488 width 0.18s cubic-bezier( 0.2, 0, 0.2, 1 ),
489 height 0.18s cubic-bezier( 0.2, 0, 0.2, 1 );
490 }
491
492 .os-snap-preview--visible {
493 opacity: 1;
494 }
495
496 /* ---------------------------------------------------------------
497 * Grid snap — the 6×6 desk shown while Option / Alt is held during a
498 * drag (see window-manager/grid-snap.ts).
499 *
500 * The overlay is the work area; the lines are one repeating gradient
501 * sized to a cell, and a cell is a fraction of the overlay — so the
502 * same rule draws the grid at any dimensions, on any display, and
503 * follows the work area if the dock folds away mid-drag. The target
504 * is the span the window will land on, in the same accent as the
505 * edge-snap preview so both previews read as one vocabulary.
506 * --------------------------------------------------------------- */
507 .os-grid-snap {
508 position: absolute;
509 /* Same layer as the edge-snap preview: under the windows, so the
510 one being dragged stays on top of its own landing zone. */
511 z-index: 99;
512 pointer-events: none;
513 opacity: 0;
514 transition: opacity 0.2s ease;
515 --os-grid-snap-cols: 6;
516 --os-grid-snap-rows: 6;
517 --os-grid-snap-line: color-mix(
518 in srgb,
519 var( --wp-admin-theme-color, #2271b1 ) 55%,
520 transparent
521 );
522 background-image:
523 linear-gradient( to right, var( --os-grid-snap-line ) 1px, transparent 1px ),
524 linear-gradient( to bottom, var( --os-grid-snap-line ) 1px, transparent 1px );
525 background-size:
526 calc( 100% / var( --os-grid-snap-cols ) ) 100%,
527 100% calc( 100% / var( --os-grid-snap-rows ) );
528 /* The gradient draws a line at the START of every cell; the
529 inset ring supplies the far edges so the grid is closed. */
530 box-shadow: inset 0 0 0 1px var( --os-grid-snap-line );
531 }
532
533 .os-grid-snap--visible {
534 opacity: 1;
535 }
536
537 .os-grid-snap__target {
538 position: absolute;
539 background: color-mix(
540 in srgb,
541 var( --wp-admin-theme-color, #2271b1 ) 16%,
542 transparent
543 );
544 border: 2px solid var( --wp-admin-theme-color, #2271b1 );
545 border-radius: 10px;
546 box-shadow:
547 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
548 0 12px 36px rgba( 0, 0, 0, 0.22 );
549 /* Geometry animates so a cell-to-cell change slides rather than
550 jumps; short, so it never lags the pointer by a cell. */
551 transition:
552 left 0.12s cubic-bezier( 0.2, 0, 0.2, 1 ),
553 top 0.12s cubic-bezier( 0.2, 0, 0.2, 1 ),
554 width 0.12s cubic-bezier( 0.2, 0, 0.2, 1 ),
555 height 0.12s cubic-bezier( 0.2, 0, 0.2, 1 );
556 }
557
558 /*
559 * The span, as "cols × rows", in the MIDDLE of the target. A user
560 * picking a 3×2 wants to know it is 3×2 before letting go; the count
561 * is what makes the grid a tool rather than a decoration, but only
562 * if it is read, and in the corner it was not. The pointer is
563 * somewhere inside the target by definition, so the centre is the
564 * shortest average trip for the eye; on a 2×5 the corner could be
565 * several hundred pixels from the hand.
566 *
567 * Centred rather than pinned to the pointer on purpose: a badge that
568 * tracked the cursor would rewrite its position every move, and a
569 * number that slides while you read it is harder to read than one
570 * that waits. It only moves when the span does.
571 */
572 .os-grid-snap__target::after {
573 content: attr( data-cols ) ' × ' attr( data-rows );
574 position: absolute;
575 top: 50%;
576 left: 50%;
577 transform: translate( -50%, -50% );
578 padding: 2px 7px;
579 border-radius: 6px;
580 background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.55 ) );
581 color: var( --os-ui-fg-on-accent, #fff );
582 font-size: 11px;
583 font-weight: 600;
584 letter-spacing: 0.03em;
585 font-variant-numeric: tabular-nums;
586 }
587
588 /*
589 * The desk in grid mode. Every OTHER window recedes so the grid and
590 * the landing zone read through them; the one in hand stays solid —
591 * it is the thing being placed, and the user needs to see it, not
592 * through it. Translucent enough that the grid shows, opaque enough
593 * that the other windows are still recognisably where they are,
594 * because "will this land on top of Posts?" is the question the grid
595 * is there to answer.
596 *
597 * Opacity only: a blur or a desaturate on a live iframe is a
598 * per-frame filter on a large surface, and the drag has to stay
599 * pixel-accurate at 120Hz. Minimized windows are already hidden and
600 * need no rule.
601 */
602 .os-area--grid-snapping .os-window:not( .os-window--grid-snapping ) {
603 opacity: 0.45;
604 transition: opacity 0.16s ease;
605 }
606
607 /* A shake moved the anchor: one pulse so the reset is seen, not inferred. */
608 .os-grid-snap__target--reset {
609 animation: os-grid-snap-reset 0.32s ease;
610 }
611
612 @keyframes os-grid-snap-reset {
613 0% {
614 box-shadow:
615 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
616 0 0 0 0 var( --wp-admin-theme-color, #2271b1 );
617 }
618 60% {
619 box-shadow:
620 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
621 0 0 0 10px transparent;
622 }
623 100% {
624 box-shadow:
625 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
626 0 12px 36px rgba( 0, 0, 0, 0.22 );
627 }
628 }
629
630 @media ( prefers-reduced-motion: reduce ) {
631 .os-grid-snap,
632 .os-grid-snap__target {
633 transition: none;
634 }
635 .os-grid-snap__target--reset {
636 animation: none;
637 }
638 }
639
640 /*
641 * Snapped window state — `--snapped-left` / `--snapped-right` carry
642 * cosmetic tweaks only. Resize handles stay visible and functional:
643 * the user can grab any corner to shrink the window back to floating
644 * (the pointer layer drops the snapped-* class on first resize so
645 * geometry stops pretending to be half-screen).
646 *
647 * Rounded-corner profile flattens the INNER edges (the ones that
648 * butt against the partner window) to match macOS split-view
649 * aesthetic.
650 *
651 * Shadows are dropped on both snapped variants. The default window
652 * drop shadow would render inside the opposite half — a dark vertical
653 * ghost line at the seam — and compete visually with the partner
654 * window. Flat edges + no shadow gives the two-up feel of a native
655 * split view: windows adjacent, no separator.
656 */
657 .os-window--snapped-left,
658 .os-window--snapped-right {
659 box-shadow: none;
660 }
661
662 /* Override the elevated shadow applied to `--focused` windows too —
663 the focused selector runs at equal specificity, so without this
664 rule the focus shadow would re-introduce the seam once the snapped
665 window is active. */
666 .os-window--focused.os-window--snapped-left,
667 .os-window--focused.os-window--snapped-right {
668 box-shadow: none;
669 }
670
671 .os-window--snapped-left {
672 /* Inner (right) edge flush; outer corners keep the radius. */
673 border-top-right-radius: 0;
674 border-bottom-right-radius: 0;
675 }
676
677 .os-window--snapped-right {
678 border-top-left-radius: 0;
679 border-bottom-left-radius: 0;
680 }
681
682 /*
683 * Split overview — a constrained variant of the full overview,
684 * scoped to the half opposite a just-snapped window. Uses the same
685 * thumbnail visuals as the main overview; only the backdrop tint is
686 * lighter (the anchor window still takes the other half, so we want
687 * the picker to feel "peripheral" not "modal").
688 */
689 .os-area--split-overview {
690 background-color: rgba( 0, 0, 0, 0.28 );
691 transition: background-color 0.24s ease;
692 }
693
694 /* Toast styling moved into `<os-toast-container>` and `<os-toast>`
695 * (src/ui/components/os-toast/). The components own their shadow
696 * DOM styling; nothing at host level needs to reach inside. */
697