/**
* Desktop Mode — Shell Layout.
*
* When desktop mode is active, the classic admin chrome still renders
* server-side (for hook compatibility) but is hidden with CSS. The
* plugin-injected shell floats above it, positioned just below the
* fixed admin bar, covering the rest of the viewport.
*
* @since 0.1.0
*/
/* Solid backdrop behind the shell. */
body.desktop-mode-active {
background: #1d2327;
overflow: hidden;
}
/*
* Cursor policy — default arrow everywhere in desktop mode.
*
* Per user preference (since 0.20.0): no hand/pointer/grab/grabbing/
* copy/no-drop cursors on icons, tiles, dock items, dock peek,
* window chrome, or any other clickable surface. The default arrow
* is the only "neutral" cursor we render.
*
* Exceptions:
* - Window + widget resize handles keep their direction-specific
* cursors (ns-resize, ew-resize, nesw-resize, nwse-resize) —
* they're a functional cue, not a hover affordance, and removing
* them would make corner-grabs guesswork.
* - Text inputs / textareas / contenteditable surfaces keep the
* browser-default I-beam.
* - The AI assistant's inline "AI Settings" recovery link keeps a
* pointer so it reads as a genuine link (rule lives in
* ai-assistant.css).
*
* `!important` is needed because the specific `cursor: pointer` and
* drag-state declarations elsewhere in the codebase have the same or
* higher specificity. The resize-handle exceptions use `!important`
* for the same reason.
*
* Shadow-DOM web components (`wpd-*`) define their own cursors
* inside their shadow trees; those internal rules win regardless of
* what we set on the host. Anything visible on the desktop shell
* (light DOM) is covered by this policy.
*/
body.desktop-mode-active,
body.desktop-mode-active * {
cursor: default !important;
}
body.desktop-mode-active input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([type="image"]),
body.desktop-mode-active textarea,
body.desktop-mode-active [contenteditable=""],
body.desktop-mode-active [contenteditable="true"] {
cursor: text !important;
}
body.desktop-mode-active .desktop-mode-window__resize-handle--se,
body.desktop-mode-active .desktop-mode-window__resize-handle--nw,
body.desktop-mode-active .desktop-mode-widgets__resize--se,
body.desktop-mode-active .desktop-mode-widgets__resize--nw {
cursor: nwse-resize !important;
}
body.desktop-mode-active .desktop-mode-window__resize-handle--sw,
body.desktop-mode-active .desktop-mode-window__resize-handle--ne,
body.desktop-mode-active .desktop-mode-widgets__resize--sw,
body.desktop-mode-active .desktop-mode-widgets__resize--ne {
cursor: nesw-resize !important;
}
body.desktop-mode-active .desktop-mode-widgets__resize--n,
body.desktop-mode-active .desktop-mode-widgets__resize--s {
cursor: ns-resize !important;
}
body.desktop-mode-active .desktop-mode-widgets__resize--e,
body.desktop-mode-active .desktop-mode-widgets__resize--w {
cursor: ew-resize !important;
}
/*
* Hide the classic admin chrome while the shell is active.
*
* The markup still exists in the DOM so server-side action hooks that
* target these regions (admin_notices, in_admin_footer, etc.) continue
* to fire — we just take them out of the visual flow.
*/
body.desktop-mode-active #adminmenuwrap,
body.desktop-mode-active #adminmenuback,
body.desktop-mode-active #wpbody,
body.desktop-mode-active #wpfooter,
body.desktop-mode-active .wp-responsive-toggle,
body.desktop-mode-active #collapse-menu {
display: none !important;
}
/*
* The admin bar is the ONLY escape hatch in desktop mode — it hosts the
* "Switch to Classic Admin" toggle. Any plugin CSS, Gutenberg state,
* user preference, or stray `display:none` that hides it would strand
* the user inside the shell with no way out. Hard-pin it visible.
*
* The exception: while a window is in immersive fullscreen, we *want*
* the bar hidden (macOS convention). That rule lives below and wins on
* specificity because it also targets #wpadminbar under a body class.
*/
body.desktop-mode-active #wpadminbar {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
position: fixed !important;
inset-block-start: 0 !important;
inset-inline-start: 0 !important;
inset-inline-end: 0 !important;
}
/*
* Align the admin-bar W logo with the dock below it.
*
* Without this rule the admin bar's leftmost item (the WordPress W)
* renders at its Core-dictated ~44 px width, while the dock below it
* can be 48, 56, or 72 px wide depending on the user's OS-Settings
* preference. The misalignment reads as "two disconnected left
* regions" — visually scruffy on an otherwise tight shell.
*
* We size the logo's
to match the dock's width and center the
* W inside, so the two columns share an axis. `--desktop-mode-dock-width`
* is now set on :root (see settings.ts → apply()) so it reaches
* siblings of #desktop-mode-shell like the admin bar.
*/
body.desktop-mode-active #wpadminbar #wp-admin-bar-wp-logo {
width: var( --desktop-mode-dock-width, 56px );
}
body.desktop-mode-active #wpadminbar #wp-admin-bar-wp-logo > .ab-item {
display: flex !important;
align-items: center;
justify-content: center;
padding-inline: 0 !important;
}
/*
* The W glyph is painted via a ::before pseudo on `.ab-icon`. Center
* it within the flex cell so varying dock widths don't leave it
* visually offset.
*/
body.desktop-mode-active #wpadminbar #wp-admin-bar-wp-logo > .ab-item > .ab-icon {
margin: 0;
}
/*
* Classic wpcontent carries a `margin-left: 160px` for the sidebar. With
* the sidebar hidden we let wpcontent fall back to flush — the shell is
* fixed-positioned anyway so this doesn't change the shell's layout, it
* just prevents a phantom sidebar gutter in any stray visible content.
*/
body.desktop-mode-active #wpcontent {
margin-inline-start: 0;
padding-inline-start: 0;
}
/*
* Desktop shell — fills the viewport below the admin bar.
* Uses position: fixed so it's unaffected by any containing-block
* quirks in #wpwrap/#wpcontent.
*/
.desktop-mode-shell {
position: fixed;
inset-block-start: var(--wp-admin--admin-bar--height, 32px);
inset-inline-start: 0;
inset-inline-end: 0;
inset-block-end: 0;
display: flex;
flex-direction: column;
overflow: hidden;
z-index: var(--desktop-mode-z-base);
}
/*
* Wallpaper layer — first child of the shell, sits behind the dock
* and desktop area so a translucent dock shows through to the
* background (macOS pattern). CSS wallpapers paint via the
* `--desktop-mode-bg` custom property; canvas wallpapers mount their
* own DOM (typically a