/** * My WordPress — the content explorer app. * * Injected on the window's first open, after the runtime's own sheet. * Every colour resolves through the palette tokens with the pre-brand * literal as the floor, per the variables.css contract. */ .os-mywp { display: flex; flex-direction: column; block-size: 100%; font-family: var( --os-font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif ); /* The SAME window-token family WP Explorer wears * (`--os-my-wordpress-*`), so a desktop theme that repaints one * explorer repaints both — with the palette chain underneath and * the pre-brand literal as the floor. */ color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) ); background: var( --os-my-wordpress-bg, var( --os-window-bg, #fff ) ); /* * Tile token re-scope — the same recipe as WP Explorer. The * shared `.os-file-tile` is born on the WALLPAPER, where its * label is white over a dark gradient; inside a window those * defaults are invisible on a light surface. Re-pointing the * `--os-tile-*` tokens here retones the one shared element for * in-window reading, and keeps it on the theme chain. */ --os-tile-fg: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) ); --os-tile-fg-muted: var( --os-my-wordpress-fg-muted, var( --os-ui-fg-muted, rgba( 0, 0, 0, 0.55 ) ) ); --os-tile-hover-bg: var( --os-hover, rgba( 0, 0, 0, 0.06 ) ); --os-tile-label-shadow: none; --os-tile-label-weight: 500; --os-tile-label-smoothing: antialiased; --os-tile-label-color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) ); } /* ------------------------------------------------------------ header */ .os-mywp__header { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-block-end: 1px solid var( --os-ui-border, #dcdcde ); flex: 0 0 auto; } .os-mywp__back { flex: 0 0 auto; inline-size: 28px; block-size: 28px; display: grid; place-items: center; border: 0; border-radius: 6px; background: transparent; color: var( --os-ui-fg-muted, #646970 ); font: inherit; font-size: 18px; line-height: 1; cursor: pointer; } .os-mywp__back:hover, .os-mywp__back:focus-visible { /* The shared breadcrumb back-button hover chain * (`.os-breadcrumbs__back` in desktop-files.css). */ background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) ); color: inherit; } .os-mywp__crumbs { display: flex; align-items: center; gap: 8px; min-inline-size: 0; flex: 1 1 auto; font-size: 13px; } .os-mywp__crumb-link { border: 0; padding: 0; background: transparent; font: inherit; /* WP Explorer's link chain: the palette's link colour first, the * accent next, the pre-brand admin blue as the floor. */ color: var( --os-link, var( --os-ui-accent, #2271b1 ) ); cursor: pointer; } .os-mywp__crumb-link:hover, .os-mywp__crumb-link:focus-visible { text-decoration: underline; } .os-mywp__crumb-current { font-weight: 600; } .os-mywp__sep { color: var( --os-ui-fg-muted, #646970 ); } /* Search row — its own band under the header, WP Explorer style. * The view switch sits at its far end. */ .os-mywp__add-user { flex: 0 0 auto; } .os-mywp__search { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; flex: 0 0 auto; border-block-end: 1px solid var( --os-ui-border, #dcdcde ); } .os-mywp__search os-text-field { inline-size: min( 360px, 100% ); display: block; flex: 1 1 auto; } .os-mywp__view-switch { flex: 0 0 auto; } .os-mywp__view-switch .dashicons { font-size: 16px; inline-size: 16px; block-size: 16px; vertical-align: -3px; } /* Under ~560px the switch keeps its glyphs and drops the words. */ @container ( max-width: 560px ) { .os-mywp__view-label { display: none; } } /* -------------------------------------------------------------- body */ .os-mywp__body { flex: 1 1 auto; overflow: hidden; display: flex; flex-direction: column; } /* -------------------------------------------------------------- root */ /* * The group-extras seam's container — a plugin panel above a folder's * tiles (Woo's Store totals). The panel inside brings its own chrome; * this only gives it the folder grid's gutter, and only when a * subscriber actually painted something. */ .os-mywp__body > .os-my-wordpress__group-extras:not( :empty ) { padding: 16px 16px 0; } .os-mywp__root { display: grid; grid-template-columns: repeat( auto-fill, 116px ); justify-content: start; gap: 8px; padding: 16px; overflow-y: auto; align-content: start; } .os-mywp__tile { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 14px 8px; border: 0; border-radius: 8px; background: transparent; color: inherit; font: inherit; cursor: pointer; } .os-mywp__tile:hover, .os-mywp__tile:focus-visible { background: var( --os-tile-hover-bg, rgba( 0, 0, 0, 0.06 ) ); } /* The CANONICAL selection pair every tile in the shell wears * (`.os-file-tile--selected` in desktop-files.css): the * `--os-tile-selected-bg` wash under an inset `--os-tile-focus-ring` * ring that resolves to the admin scheme colour. The root folder * tiles are plain buttons rather than ``s, so they restate * the same chains — reading `--os-ui-accent` here painted the raw * brand Pulse instead of the theme's selection, which is exactly the * divergence the shared tokens exist to prevent. */ .os-mywp__tile.is-selected { background: var( --os-tile-selected-bg, rgba( 34, 113, 177, 0.14 ) ); box-shadow: inset 0 0 0 1px var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ); } /* The icon well matches WP Explorer's `.os-file-tile__icon` / * `__preview` pair (48px box, 32px glyph), so the two windows' root * grids — the Agents portrait included — read at the same size. */ .os-mywp__tile-icon.dashicons { inline-size: 48px; block-size: 48px; font-size: 32px; display: grid; place-items: center; } .os-mywp__icon-img { inline-size: 48px; block-size: 48px; object-fit: contain; } .os-mywp__tile-label { font-size: 12px; text-align: center; overflow-wrap: anywhere; } /* ------------------------------------------------------------- split */ .os-mywp__split { display: grid; /* * WP Explorer's two-pane ratio: the list leads, the preview takes * roughly the remaining 40% and grows with the window — but never * below 430px while the window can afford it, because under that * the pane's fact tables and the Woo merchant panels wrap every * row. When the window truly can't (both floors exceed it), the * list keeps its 240px and the pane takes what is left. */ grid-template-columns: minmax( 240px, 1fr ) minmax( min( 430px, calc( 100% - 240px ) ), 40% ); flex: 1 1 auto; min-block-size: 0; } .os-mywp__list-pane { min-inline-size: 0; /* Grid items default to min-size auto; without the 0 the row grows with a tall list instead of letting the canvas scroll. */ min-block-size: 0; display: flex; flex-direction: column; } .os-mywp__detail-pane { min-inline-size: 0; min-block-size: 0; border-inline-start: 1px solid var( --os-ui-border, #dcdcde ); overflow-y: auto; } /* * A narrow window — a phone, or a desktop window pulled in under * the two floors above (240px list + 430px pane). The panes stack: * the list takes the window and the preview becomes a sheet along * the bottom, at most 45% tall and scrolling on its own, that is * there only while something is selected. An empty pane on a phone * is a bar of nothing over the list. The container is the app root * (`.os-app`, `app-runtime.css`). */ @container ( max-width: 640px ) { .os-mywp__split { grid-template-columns: minmax( 0, 1fr ); grid-template-rows: minmax( 0, 1fr ) auto; } .os-mywp__detail-pane { border-inline-start: 0; border-block-start: 1px solid var( --os-ui-border, #dcdcde ); max-block-size: 45%; } .os-mywp__detail-pane:has( > .os-mywp__pane-empty ) { display: none; } } /* * The phone's item page (`renderBody` in `my-wordpress.os.ts`): the * opened item takes the whole body, pushed over the list, with Back * in the header and the trail one step longer. The pane's own ✕ is * redundant beside Back and goes; the action row stacks so every * button is a full-width finger's target. */ .os-mywp__detail-page { flex: 1 1 auto; min-block-size: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; } .os-mywp__detail-page .os-mywp__pane-close { display: none; } .os-mywp__detail-page .os-mywp__detail { padding: 16px 16px 24px; } .os-mywp__detail-page .os-mywp__actions { flex-direction: column; align-items: stretch; align-self: stretch; } .os-mywp__detail-page .os-mywp__actions os-button { inline-size: 100%; } /* -------------------------------------------------- quick-edit modal */ .os-mywp__qe { display: flex; flex-direction: column; gap: 12px; min-inline-size: 240px; } .os-mywp__qe-row { display: flex; flex-direction: column; gap: 4px; font-size: 12px; } /* The hint above the fields — the same info notice the original's * bulk modal leads with. Ink contrast on the dark dialog is owned by * os-modal itself (it re-points --os-ui-notice-color through * --os-ui-modal-text); this rule is spacing only. */ .os-mywp__qe-notice { margin-block-end: 12px; } .os-mywp__cell.is-disabled { opacity: 0.5; } /* --------------------------------------------------------- tile grid */ /* * The same tile element the wallpaper and WP Explorer paint * (`os-tile` → `.os-file-tile`), flowed as a grid: in-window lists * are not a free canvas here, so the wallpaper's absolute * positioning is switched off and the grid owns the layout. */ /* Fixed pitch, like the original canvas: resizing the window changes * how many tiles fit per row, never how wide a tile is. */ .os-mywp__tiles { display: grid; grid-template-columns: repeat( auto-fill, 104px ); justify-content: start; gap: 10px 4px; align-content: start; padding: 12px; overflow-y: auto; flex: 1 1 auto; } .os-mywp__cell { display: flex; justify-content: center; } /* * `relative`, not `static`: the tile must join normal flow (the grid * owns layout here, not the wallpaper's absolute canvas) but it must * REMAIN the containing block of its own `` — flattened to * static, the DRAFT ribbon anchors to the grid cell and floats off * the tile's corner. */ .os-mywp__cell os-tile, .os-mywp__cell .os-file-tile { position: relative; inset: auto; } .os-mywp__tilebox { position: relative; display: inline-flex; } .os-mywp__cell.is-open .os-file-tile { outline: 1px solid var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ); outline-offset: -1px; border-radius: 8px; } .os-mywp__lock { position: absolute; inset-block-start: 2px; inset-inline-start: 2px; font-size: 12px; pointer-events: none; } /* Skeletons for the page being fetched — WP Explorer's loading * placeholders: same cell footprint, shimmering blocks. */ .os-mywp__cell--ghost { pointer-events: none; } .os-mywp__ghost { inline-size: var( --os-tile-w, 88px ); padding: 8px 4px; display: flex; flex-direction: column; align-items: center; gap: 6px; } /* The shell's skeleton fill chain (`--os-skeleton-*`, see WP * Explorer's loading placeholders), so the ghosts follow the theme * instead of guessing at a raised surface. */ .os-mywp__ghost-visual { inline-size: 48px; block-size: 48px; border-radius: 8px; background: var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) ); animation: os-mywp-shimmer 1.2s ease-in-out infinite; } .os-mywp__ghost-label { inline-size: 64px; block-size: 10px; border-radius: 5px; background: var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) ); animation: os-mywp-shimmer 1.2s ease-in-out infinite; animation-delay: 0.15s; } @keyframes os-mywp-shimmer { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } } @media ( prefers-reduced-motion: reduce ) { .os-mywp__ghost-visual, .os-mywp__ghost-label { animation: none; } } /* --------------------------------------------------------- list view */ /* * The table: the same rows as the tile canvas, in columns. The * wrapper is the scrolling canvas (both axes — a narrow window scrolls * the columns sideways rather than crushing them), the header sticks, * and every colour resolves through the same chains the tiles read so * a desktop theme repaints both views at once. */ .os-mywp__table-wrap { flex: 1 1 auto; min-block-size: 0; overflow: auto; position: relative; } /* * Auto layout with ONE stretching column: every other column is * declared 1px wide, which in an auto table means "as wide as your * (nowrap) content"; the title column takes what is left. Its * `max-inline-size: 0` is what lets the ellipsis work — an auto * layout would otherwise size the column to its longest title. No * percentage widths and no `max-content` minimum on the table: a * 100% column against a max-content floor resolves to an unbounded * width and the wrapper scrolls forever. */ .os-mywp__table { inline-size: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; table-layout: auto; } .os-mywp__th:not( .os-mywp__th--title ), .os-mywp__td:not( .os-mywp__td--title ) { inline-size: 1px; } .os-mywp__th--title, .os-mywp__td--title { inline-size: 100%; max-inline-size: 0; min-inline-size: 160px; } .os-mywp__th { position: sticky; inset-block-start: 0; z-index: 2; padding: 0 10px; block-size: 32px; text-align: start; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; color: var( --os-ui-fg-muted, #646970 ); /* The band-head recipe: near-opaque frost, so rows scrolling under * the header stay behind it. */ background: color-mix( in srgb, var( --os-ui-bg, #fff ) 94%, transparent ); backdrop-filter: blur( 14px ) saturate( 180% ); border-block-end: 1px solid var( --os-ui-border, #dcdcde ); } @supports not ( background: color-mix( in srgb, red 50%, transparent ) ) { .os-mywp__th { background: var( --os-ui-bg, #fff ); } } .os-mywp__th--end, .os-mywp__td--end { text-align: end; } .os-mywp__th, .os-mywp__td { vertical-align: middle; } /* The column-chooser button sits on the header's centre line, not on * its baseline. */ .os-mywp__th--actions { inline-size: 1px; line-height: 0; } .os-mywp__columns-btn { vertical-align: middle; } /* A sortable heading is a button wearing the heading's costume. */ .os-mywp__sort { display: inline-flex; align-items: center; gap: 4px; margin: 0; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; } .os-mywp__sort:hover, .os-mywp__sort:focus-visible, .os-mywp__sort.is-active { color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) ); } .os-mywp__sort-arrow { font-size: 8px; min-inline-size: 8px; } .os-mywp__row { cursor: default; } .os-mywp__td { padding: 0 10px; block-size: 36px; vertical-align: middle; white-space: nowrap; max-inline-size: 320px; overflow: hidden; text-overflow: ellipsis; border-block-end: 1px solid var( --os-ui-border-subtle, var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) ) ); } .os-mywp__td--mono, .os-mywp__cell-id { font-family: var( --os-ui-font-mono, ui-monospace, monospace ); font-variant-numeric: tabular-nums; font-size: 12px; } /* Hover, selection and the open row read the tile canvas's tokens — * one selection colour in the whole window, whichever view. */ .os-mywp__row:hover .os-mywp__td { background: var( --os-tile-hover-bg, rgba( 0, 0, 0, 0.06 ) ); } .os-mywp__row.is-selected .os-mywp__td { background: var( --os-tile-selected-bg, rgba( 34, 113, 177, 0.14 ) ); } .os-mywp__row.is-open .os-mywp__td { box-shadow: inset 0 -1px 0 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ), inset 0 1px 0 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ); } .os-mywp__row:focus-visible { outline: none; } .os-mywp__row:focus-visible .os-mywp__td { box-shadow: inset 0 0 0 1px var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ); } /* The id copies itself; it looks like a chip so the affordance reads. */ .os-mywp__cell-id--copy { margin: 0; padding: 2px 6px; border: 0; border-radius: 4px; background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) ); color: var( --os-my-wordpress-fg-muted, var( --os-ui-fg-muted, #646970 ) ); cursor: copy; } .os-mywp__cell-id--copy:hover, .os-mywp__cell-id--copy:focus-visible { background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) ); color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) ); } .os-mywp__cell-title { display: inline-flex; align-items: center; gap: 8px; min-inline-size: 0; max-inline-size: 100%; } .os-mywp__cell-text { overflow: hidden; text-overflow: ellipsis; font-weight: 500; } .os-mywp__cell-thumb { inline-size: 24px; block-size: 24px; border-radius: 4px; object-fit: cover; flex: 0 0 auto; background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) ); } .os-mywp__cell-glyph.dashicons { inline-size: 24px; block-size: 24px; font-size: 18px; display: grid; place-items: center; color: var( --os-ui-fg-muted, #646970 ); flex: 0 0 auto; } .os-mywp__cell-glyph.os-mywp__icon-mask { inline-size: 20px; block-size: 20px; } .os-mywp__cell-badge { flex: 0 0 auto; } .os-mywp__cell-lock { font-size: 14px; inline-size: 14px; block-size: 14px; color: var( --os-ui-fg-muted, #646970 ); } .os-mywp__cell-muted { color: var( --os-ui-fg-muted, #646970 ); } .os-mywp__cell-parent .os-mywp__cell-id { color: var( --os-ui-fg-muted, #646970 ); } /* The action cluster: icon-only ghost buttons, visible on hover, on * the selected row, and whenever one of them holds focus. */ .os-mywp__row-actions { display: inline-flex; gap: 2px; opacity: 0; transition: opacity 120ms ease; } .os-mywp__row:hover .os-mywp__row-actions, .os-mywp__row.is-selected .os-mywp__row-actions, .os-mywp__row.is-open .os-mywp__row-actions, .os-mywp__row-actions:focus-within { opacity: 1; } @media ( prefers-reduced-motion: reduce ) { .os-mywp__row-actions { transition: none; } } .os-mywp__row-action { --os-ui-button-padding: 3px; --os-ui-button-border-radius: 4px; line-height: 0; } .os-mywp__row-action .dashicons { font-size: 16px; inline-size: 16px; block-size: 16px; } .os-mywp__columns-btn { opacity: 1; } /* Skeleton rows for the page being fetched — the tile ghosts' fill. */ .os-mywp__row--ghost td { padding: 10px; border-block-end: 1px solid var( --os-ui-border-subtle, var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) ) ); } .os-mywp__ghost-line { display: block; block-size: 12px; inline-size: min( 60%, 420px ); border-radius: 6px; background: var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) ); animation: os-mywp-shimmer 1.2s ease-in-out infinite; } @media ( prefers-reduced-motion: reduce ) { .os-mywp__ghost-line { animation: none; } } .os-mywp__table-empty { margin: 0; padding: 32px 16px; text-align: center; color: var( --os-ui-fg-muted, #646970 ); } /* ------------------------------------------------------- list bands */ /* Banded layout — the `os.my-wordpress.list-bands` filter's canvas. * The scroller becomes a column of band sections, each with its own * grid at the same pitch as the flat canvas; headings mirror WP * Explorer's band bars (sticky, frosted, tone-tintable). */ .os-mywp__tiles--banded { display: flex; flex-direction: column; gap: 4px; } .os-mywp__band + .os-mywp__band { margin-block-start: 8px; } .os-mywp__band-head { display: flex; align-items: center; gap: 8px; position: sticky; /* Keeps the band label visible while scrolling a long band — * the whole point of banding is knowing what you're looking at. */ inset-block-start: 0; z-index: 3; margin: 0; padding: 6px 12px; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var( --os-ui-fg-muted, #787c82 ); /* Near-opaque frosted bar, WP Explorer's recipe: at 94% the tiles * scrolling beneath stay behind the bar instead of resolving into * ghostly shapes through it. */ background: color-mix( in srgb, var( --os-ui-bg, #fff ) 94%, transparent ); backdrop-filter: blur( 14px ) saturate( 180% ); border-block-end: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); } @supports not ( background: color-mix( in srgb, red 50%, transparent ) ) { .os-mywp__band-head { background: var( --os-ui-bg, #fff ); } } /* Attention-carrying bands tint their bar so the eye lands there * first instead of having to read every heading. */ .os-mywp__band-head--warn { color: var( --os-ui-warning, #8a6116 ); background: color-mix( in srgb, var( --os-ui-warning, #8a6116 ) 12%, var( --os-ui-bg, #fff ) ); border-block-end-color: color-mix( in srgb, var( --os-ui-warning, #8a6116 ) 35%, transparent ); } .os-mywp__band-head--danger { color: var( --os-ui-danger, #b32d2e ); background: color-mix( in srgb, var( --os-ui-danger, #b32d2e ) 12%, var( --os-ui-bg, #fff ) ); border-block-end-color: color-mix( in srgb, var( --os-ui-danger, #b32d2e ) 35%, transparent ); } .os-mywp__band-count { padding: 0 6px; border-radius: 999px; font-size: 11px; line-height: 1.7; background: color-mix( in srgb, currentColor 16%, transparent ); color: inherit; } .os-mywp__band-grid { display: grid; grid-template-columns: repeat( auto-fill, 104px ); justify-content: start; gap: 10px 4px; align-content: start; padding: 12px; } /* ------------------------------------------------------------ detail */ .os-mywp__detail { position: relative; padding: 16px; display: flex; flex-direction: column; gap: 12px; align-items: flex-start; } .os-mywp__pane-close { position: absolute; inset-block-start: 8px; inset-inline-end: 8px; } /* Plugin slots on the article (`os.my-wordpress.preview-extras`). * The article is a gap-spaced column, so a slot nobody painted into * must not spend a gap step. Scoped to OUR pane — WP Explorer's own * slots keep their stylesheet's rules. */ .os-mywp__detail .os-my-wordpress__article-slot { align-self: stretch; } .os-mywp__detail .os-my-wordpress__article-slot:empty { display: none; } .os-mywp__hero { max-inline-size: 100%; max-block-size: 200px; border-radius: 8px; object-fit: contain; } .os-mywp__detail-title { margin: 0; font-size: 17px; padding-inline-end: 32px; } /* The identity line under a person's name: role badge + archive link. */ .os-mywp__user-meta { display: flex; align-items: center; gap: 10px; margin: 0; } .os-mywp__facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 0; font-size: 13px; } .os-mywp__fact { display: contents; } .os-mywp__fact dt { color: var( --os-ui-fg-muted, #646970 ); } .os-mywp__fact dd { margin: 0; } .os-mywp__pane-h { margin: 4px 0 0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var( --os-ui-fg-muted, #646970 ); } .os-mywp__used-in { margin: 0; padding-inline-start: 18px; font-size: 13px; } .os-mywp__actions { display: flex; gap: 8px; flex-wrap: wrap; } .os-mywp__content { max-inline-size: 100%; overflow-wrap: anywhere; font-size: 13px; } .os-mywp__content img { max-inline-size: 100%; block-size: auto; } .os-mywp__hero.is-zoomable { cursor: zoom-in; } .os-mywp__sentinel { block-size: 1px; } /* The hover card (.os-my-wordpress__tooltip and friends) is painted * by the SHARED explorer stylesheet, which rides this window as a * companion style (includes/my-wordpress/assets.php) — one set of * rules for both explorers' cards, not a copy here. */ /* ---------------------------------------------------- marquee + menu */ /* The SAME token pair the shell's rubber band wears * (desktop-files.css): a ring on the admin scheme colour over the * shared marquee wash — a WASH, so you can see what it is about to * catch. */ .os-mywp__marquee { position: fixed; z-index: 100000; border: 1px solid var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ); border-radius: 2px; background: var( --os-selection-marquee-bg, var( --os-ui-accent-soft, rgba( 34, 113, 177, 0.14 ) ) ); pointer-events: none; } .os-mywp__menu-backdrop { position: fixed; inset: 0; z-index: 99998; } .os-mywp__menu { z-index: 99999; } /* -------------------------------------------------------------- zoom */ .os-mywp__zoom { position: fixed; inset: 0; z-index: 100000; display: grid; place-items: center; background: rgba( 0, 0, 0, 0.8 ); cursor: zoom-out; } .os-mywp__zoom img { max-inline-size: 92vw; max-block-size: 92vh; object-fit: contain; } /* -------------------------------------------- sub-list stats panes */ .os-mywp__icon-mask { inline-size: 48px; block-size: 48px; display: inline-block; background: currentColor; -webkit-mask: var( --mywp-icon ) center / contain no-repeat; mask: var( --mywp-icon ) center / contain no-repeat; } .os-mywp__term-head { display: flex; align-items: flex-start; gap: 12px; } .os-mywp__term-head div { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; } /* WP Explorer's taxonomy medallion, tokens and shadow included * (`.os-my-wordpress__term-icon--category`). */ .os-mywp__term-swatch { inline-size: 48px; block-size: 48px; border-radius: 12px; background: linear-gradient( 135deg, var( --os-ui-accent, #2271b1 ) 0%, var( --os-ui-accent-strong, #135e96 ) 100% ); box-shadow: 0 4px 12px rgba( 34, 113, 177, 0.32 ); flex: 0 0 auto; } .os-mywp__stats { display: grid; grid-template-columns: repeat( auto-fill, minmax( 110px, 1fr ) ); gap: 8px; inline-size: 100%; } /* The stat tiles are the kit's , on its defaults — the accent-coloured value is the same --wp-admin-theme-color chain WP Explorer's stat values read, declared in the component. */ .os-mywp__activity { display: flex; align-items: flex-end; gap: 4px; block-size: 72px; inline-size: 100%; } .os-mywp__activity-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; block-size: 100%; gap: 2px; min-inline-size: 0; } .os-mywp__activity-bar { inline-size: 100%; min-block-size: 1px; border-radius: 3px 3px 0 0; /* WP Explorer's sparkline gradient, verbatim. */ background: linear-gradient( 180deg, var( --wp-admin-theme-color, #2271b1 ) 0%, rgba( 34, 113, 177, 0.55 ) 100% ); } .os-mywp__activity-label { font-size: 9px; color: var( --os-ui-fg-muted, #646970 ); } .os-mywp__recent { display: flex; flex-direction: column; gap: 4px; inline-size: 100%; } .os-mywp__recent-row { display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; border: 0; border-radius: 6px; background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) ); color: inherit; font: inherit; text-align: start; cursor: pointer; } .os-mywp__recent-row:hover, .os-mywp__recent-row:focus-visible { background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) ); } .os-mywp__recent-title { font-weight: 600; color: var( --os-link, var( --os-ui-accent, #2271b1 ) ); } .os-mywp__people { display: flex; flex-direction: column; gap: 6px; inline-size: 100%; } .os-mywp__person { display: flex; align-items: center; gap: 8px; } .os-mywp__person-name { font-weight: 600; flex: 1 1 auto; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-mywp__chips { display: flex; flex-wrap: wrap; gap: 6px; } .os-mywp__chip { border: 1px solid var( --os-ui-border, #dcdcde ); border-radius: 999px; padding: 3px 10px; background: transparent; color: inherit; font: inherit; font-size: 12px; cursor: pointer; } .os-mywp__chip:hover, .os-mywp__chip:focus-visible { background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) ); } /* The user dossier's top-terms chips are facts, not doors — no hover. */ .os-mywp__chip--static:hover { background: transparent; cursor: default; } /* ------------------------------------------------------------ status */ .os-mywp__status { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 4px 12px; font-size: 11px; color: var( --os-ui-fg-muted, #646970 ); border-block-start: 1px solid var( --os-ui-border, #dcdcde ); flex: 0 0 auto; } .os-mywp__pane-empty { margin: 0; block-size: 100%; display: grid; place-items: center; padding: 24px; text-align: center; color: var( --os-ui-fg-muted, #646970 ); font-size: 13px; } /* ------------------------------------------------------------------ */ /* Agents section — WP Explorer's `dm-agents` design, ported 1:1 */ /* (assets/css/my-wordpress.css is the original; same class names, */ /* same tokens, so a desktop theme that repaints one repaints both). */ /* ------------------------------------------------------------------ */ .dm-agents { display: flex; flex-direction: column; height: 100%; min-height: 0; padding: 12px; gap: 10px; overflow: hidden; box-sizing: border-box; } .dm-agents__detail { flex: 1; min-width: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; } /* `` owns the icon + copy and centres them within itself; the host is still a flex item that shrinks to its content, so without this it sits at the top of a pane that is mostly empty space. Auto margins on both axes absorb the slack — placement only, same as the comments window. Covers the framework-off state and "No agents yet" alike. */ .dm-agents__detail > os-empty-state { margin: auto; } .dm-agents__detail-head { display: flex; align-items: center; gap: 10px; } .dm-agents__detail-avatar { width: 48px; height: 48px; border-radius: 50%; /* Placeholder behind the avatar image while it loads — follows the palette so it isn't a white disc on a dark station. */ background: var( --os-ui-surface-elevated, #fff ); flex: none; } .dm-agents__detail-title { flex: 1; min-width: 0; } .dm-agents__detail-title h3 { margin: 0; font-size: 16px; } .dm-agents__detail-slug { font-size: 12px; opacity: 0.6; } /* Verbs get their own row under the identity block: the set grows (profile, contributions, desktop, chat, delete) and squeezing them beside the name truncated it first. */ .dm-agents__detail-actions { display: flex; flex-wrap: wrap; gap: 8px; } /* The pane strip is the kit's own ; nothing to restyle. */ .dm-agents__pane { display: flex; flex-direction: column; gap: 10px; padding-bottom: 12px; } .dm-agents__hint { margin: 0; font-size: 12px; opacity: 0.7; } /* Role first: least privilege is the decision, so the control holds the mockup's width instead of stretching into a landing strip. */ .dm-agents__role-select { max-width: 280px; } /* The ability filter. Sized like the role select rather than the full pane: it sits above a list of rules, and a search box as wide as the rules reads as another one of them. */ .dm-agents__ability-search { display: block; max-width: 320px; margin: 12px 0 4px; } /* Each category is a native disclosure. `
` because the behaviour is exactly what it is for: keyboard, screen-reader semantics and the open/closed state all arrive for free, and the only thing left to write is the costume. */ .dm-agents__ability-group { border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) ); } .dm-agents__ability-group:last-of-type { border-bottom: 0; } .dm-agents__category { margin: 18px 0 2px; font-family: var( --os-ui-font-mono, ui-monospace, monospace ); font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.08em; color: var( --os-ui-fg-muted, #646970 ); } /* A `` is a button, so it gets a button's affordances: a pointer, a hover, and a focus ring that is visible on the dark surface the section is drawn on. */ summary.dm-agents__category { display: flex; align-items: center; gap: 8px; margin: 0; padding: 10px 0; cursor: pointer; list-style: none; user-select: none; } summary.dm-agents__category::-webkit-details-marker { display: none; } /* The marker is drawn here rather than left to the UA so it can be the same triangle in every browser and turn with the disclosure. Rotation only: a transform, so opening a group of forty rows costs no layout. */ summary.dm-agents__category::before { content: ''; flex: none; width: 0; height: 0; border-inline-start: 5px solid currentcolor; border-block: 4px solid transparent; transition: transform 120ms ease; } .dm-agents__ability-group[ open ] > summary.dm-agents__category::before { transform: rotate( 90deg ); } @media ( prefers-reduced-motion: reduce ) { summary.dm-agents__category::before { transition: none; } } summary.dm-agents__category:hover { color: var( --os-ui-fg, #1e1e1e ); } summary.dm-agents__category:focus-visible { outline: 2px solid var( --os-ui-accent, #3858e9 ); outline-offset: 2px; border-radius: 4px; } .dm-agents__category-name { flex: 1 1 auto; } /* "3 of 12" when some are ticked, otherwise just the size of the group. The count is why a closed group is still informative: collapsing must not hide the fact that something in there is switched on. */ .dm-agents__category-count { flex: none; font-variant-numeric: tabular-nums; opacity: 0.75; } /* One ability per row, ruled like the mockup: tick and name on the left, badge on the right, the description indented under the name (the checkbox column is the control's own 26px). */ .dm-agents__ability { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: start; padding: 11px 0; border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) ); } .dm-agents__ability:last-of-type { border-bottom: 0; } .dm-agents__ability-desc { grid-column: 1 / -1; margin: 0; padding-inline-start: 26px; font-size: 12.5px; color: var( --os-ui-fg-muted, #646970 ); } /* Access badges in the instrument voice. The palette's own warning tokens carry the pale-yellow "can modify"; all this changes is the costume: mono, uppercase, an outline instead of a wash. */ .dm-agents__ability os-badge { --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace ); --os-ui-badge-dot-size: 0; --os-ui-badge-gap: 0; --os-ui-badge-padding: 3px 8px; --os-ui-badge-bg: transparent; --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) ); text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; margin-top: 2px; } .dm-agents__ability os-badge[ tone='warning' ] { --os-ui-badge-border: 1px solid var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) ); } .dm-agents__trigger { border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) ); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; } .dm-agents__trigger-head { display: flex; align-items: center; gap: 8px; } .dm-agents__trigger-desc { margin: 0; font-size: 12px; opacity: 0.75; } .dm-agents__trigger-config { display: flex; flex-wrap: wrap; gap: 4px 14px; } .dm-agents__actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-block-start: 12px; } /* --------------------------------------------------------------------- The cast ------------------------------------------------------------------ */ /* One view at a time: the grid, an agent, or the wizard. A sidebar of faces next to a detail form would leave neither enough room, and the whole point of the grid is that the faces are big enough to tell apart. */ .dm-agents__view { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow-y: auto; gap: 12px; } .dm-agents__cast-head { display: flex; align-items: baseline; gap: 8px; } .dm-agents__cast-head h3 { margin: 0; } .dm-agents__cast-count { font-family: var( --os-ui-font-mono, ui-monospace, monospace ); font-size: 12px; color: var( --os-ui-fg-muted, #646970 ); } .dm-agents__cast { display: grid; gap: 16px; grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) ); align-content: start; } /* The card reacts like the mockup's: a lift and a brighter edge. Transform and border-color only — both cheap, and the same pair the mockup transitions. Document styles win over the component's own :host rules, which is the sanctioned way to restyle a host. */ .dm-agents__cast-card, .dm-agents__cast-new { --os-ui-card-padding: 20px 18px 16px; transition: border-color 0.18s ease, transform 0.18s ease; } .dm-agents__cast-card:hover, .dm-agents__cast-card:focus-within { border-color: var( --os-ui-border-strong, #8c8f94 ); transform: translateY( -2px ); } /* The dashed door, same as the mockup's crew-new card. */ .dm-agents__cast-new { border-style: dashed; --os-ui-card-bg: var( --os-ui-surface-subtle, transparent ); } /* The crew breathes. Transform only, staggered so five faces do not bob in lockstep, and gone entirely under reduced motion. */ @media ( prefers-reduced-motion: no-preference ) { .dm-agents__cast-card .dm-agents__cast-face { animation: dm-agents-bob 6s ease-in-out infinite; } .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face { animation-duration: 7.2s; animation-delay: -1.4s; } .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face { animation-duration: 6.6s; animation-delay: -2.8s; } } @keyframes dm-agents-bob { 0%, 100% { transform: translateY( 0 ); } 50% { transform: translateY( -4px ); } } /* The column lives on a wrapper we own, not on the host: the slotted children sit inside the card's own container, so a flex rule on the host never reaches them and the role badges land wherever the description's line count leaves them. */ .dm-agents__cast-inner { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px; height: 100%; } /* The faces are the loud part of this surface, and deliberately so: agent identity is the one place the station spends the mesh budget on something other than a hero CTA. A cast has to be legible at a glance, which is exactly what a flat accent cannot do. */ .dm-agents__cast-face { width: 118px; height: 118px; margin: -10px 0 6px; flex: none; } .dm-agents__cast-name { font-size: 17px; font-weight: 600; color: var( --os-ui-fg, #1d2327 ); } /* The voice line, in the instrument register: this is metadata about the agent, not prose from it. */ .dm-agents__cast-vibes { font-family: var( --os-ui-font-mono, ui-monospace, monospace ); font-size: 11.5px; color: var( --os-ui-accent-strong, #2271b1 ); } .dm-agents__cast-good { font-size: 13px; color: var( --os-ui-fg-muted, #646970 ); margin-block: 4px 14px; flex: 1; } /* The role pill in the instrument voice: mono, uppercase, no status dot — this is a fact about the agent, not a state to monitor. The custom properties are the component's own theming hooks; the inherited text properties flow into its shadow label. */ .dm-agents__cast-card os-badge { --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace ); --os-ui-badge-dot-size: 0; --os-ui-badge-gap: 0; --os-ui-badge-padding: 3px 10px; text-transform: uppercase; letter-spacing: 0.08em; } /* The framework-off bar: the section's status and its one action, on one line above the crew it would hire. It is deliberately not an `os-empty-state` — that block is centred, icon-led and paragraph- shaped, which is exactly the wall of text the cast replaced, and at five cards tall it would push the button that undoes the situation off the bottom of the window. `flex: none` because `.dm-agents__view` is a scrolling flex column and the bar must not absorb the slack. */ .dm-agents__off-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; flex: none; gap: 8px 16px; padding: 12px 14px; border-radius: 10px; background: var( --os-ui-surface-elevated, #fff ); border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) ); } .dm-agents__off-copy { min-width: 0; } .dm-agents__off-copy h3 { margin: 0; } .dm-agents__off-copy p { margin: 2px 0 0; font-size: 13px; color: var( --os-ui-fg-muted, #646970 ); } /* The cast the site would get, drawn while the framework is off. Dimmed as a whole rather than desaturated: the five hues ARE the argument this state is making, and a greyscale filter would flatten the crew into five identical blobs on the way to saying "not yet". Opacity says inactive and leaves the faces telling each other apart. `pointer-events` is belt and braces — the cards are rendered without `interactive` and without a click handler, so there is nothing to press. It exists so a hover state cannot promise otherwise. */ .dm-agents__cast--preview { opacity: 0.6; pointer-events: none; } .dm-agents__cast-new .dm-agents__cast-plus { display: grid; place-items: center; width: 118px; height: 118px; margin: -10px 0 6px; border-radius: 50%; border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 ); font-size: 30px; font-weight: 300; line-height: 1; color: var( --os-ui-fg-muted, #646970 ); } /* --------------------------------------------------------------------- The wizard ------------------------------------------------------------------ */ .dm-agents__wizard { display: flex; flex-direction: column; gap: 10px; } .dm-agents__wiz-head { display: flex; align-items: center; justify-content: space-between; gap: 8px 12px; flex-wrap: wrap; } .dm-agents__wiz-head h3 { margin: 0; } /* The trail wears the instrument voice: mono uppercase labels, 22px dots, a thin rule between stations. All of it goes through the component's own theming hooks; the host elements live in this light DOM, so the per-state overrides below can select them directly. */ .dm-agents__trail { margin-block: 6px 14px; --os-ui-steps-gap: 10px; --os-ui-step-gap: 7px; --os-ui-step-chip-size: 22px; --os-ui-step-chip-font-size: 11px; --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace ); --os-ui-step-title-size: 11px; --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace ); --os-ui-step-title-transform: uppercase; --os-ui-step-title-spacing: 0.08em; } /* A station you have not reached is an outline, not a filled dot: the mesh chip is where you ARE, and it only reads that way if it is the only one lit. */ .dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) { --os-ui-step-chip-bg: transparent; --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 ); --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 ); } /* Done is quiet: a filled tick on the elevated surface, muted like its label. It reads as history, not as a second highlight. */ .dm-agents__trail os-step[ done ] { --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 ); --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 ); } .dm-agents__wiz-heading { margin: 12px 0 2px; font-size: 16px; font-weight: 600; color: var( --os-ui-fg, #1d2327 ); } /* The door. Five complete agents already ship with the plugin, and until now the create flow showed them to nobody. */ .dm-agents__starters { display: grid; gap: 12px; grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) ); margin-block: 8px 16px; } .dm-agents__starter { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 3px; --os-ui-card-padding: 16px 14px; --os-ui-card-radius: 10px; --os-ui-card-bg: var( --os-ui-surface-subtle, transparent ); transition: border-color 0.18s ease; } .dm-agents__starter:hover, .dm-agents__starter:focus-within { border-color: var( --os-ui-border-strong, #8c8f94 ); } .dm-agents__starter-face { width: 76px; height: 76px; margin: -8px 0 4px; flex: none; } .dm-agents__starter-name { font-size: 14px; font-weight: 500; color: var( --os-ui-fg, #1d2327 ); } .dm-agents__starter-vibes { font-family: var( --os-ui-font-mono, ui-monospace, monospace ); font-size: 11.5px; color: var( --os-ui-fg-muted, #646970 ); } /* Meet: the portrait beside the fields, so the thing being named is visible while it is being named. */ .dm-agents__meet { display: grid; gap: 20px; grid-template-columns: 220px 1fr; align-items: start; } /* The character card. Sunken rather than subtle: the portrait is the hero of this step, and the mockup floats it on the darkest surface so the face's own glow does the framing. */ .dm-agents__portrait { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 20px 14px 14px; border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) ); border-radius: var( --os-ui-card-radius, 10px ); background: var( --os-ui-surface-sunken, #f6f7f7 ); } .dm-agents__portrait-face { width: 176px; height: 176px; max-width: 100%; margin-top: -12px; } /* What this face is, in two words. */ .dm-agents__portrait-chips { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; } .dm-agents__faces { display: grid; grid-template-columns: repeat( 6, 1fr ); gap: 2px; width: 100%; } .dm-agents__face-pick { display: grid; place-items: center; padding: 2px; border: 1px solid transparent; border-radius: 8px; background: none; cursor: pointer; } .dm-agents__face-pick img { width: 100%; height: auto; display: block; } .dm-agents__face-pick:hover { border-color: var( --os-ui-border-strong, #8c8f94 ); } .dm-agents__face-pick.is-picked { border-color: var( --os-ui-accent, #2271b1 ); } .dm-agents__face-pick:focus-visible { outline: var( --os-ui-focus-ring, 2px solid #2271b1 ); outline-offset: 1px; } .dm-agents__meet-fields { display: flex; flex-direction: column; gap: 10px; min-width: 0; } /* Launch: one card, the brand's card radius, floating on the darkest surface like the portrait it echoes. */ .dm-agents__summary { display: grid; grid-template-columns: 106px 1fr; gap: 18px; align-items: start; --os-ui-card-padding: 18px; --os-ui-card-radius: 10px; --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 ); } .dm-agents__summary-face { width: 106px; height: 106px; flex: none; } .dm-agents__summary-text h4 { margin: 0 0 2px; font-size: 17px; } .dm-agents__summary-vibes { font-family: var( --os-ui-font-mono, ui-monospace, monospace ); font-size: 11.5px; color: var( --os-ui-accent-strong, #2271b1 ); margin: 0 0 6px; } .dm-agents__summary-desc { margin: 0; font-size: 13px; color: var( --os-ui-fg-muted, #646970 ); } .dm-agents__chips { display: flex; flex-wrap: wrap; gap: 6px; margin-block-start: 10px; } /* What the agent will actually be told, under the chips. Clamped: a summary card quotes the instructions, it does not host them. */ .dm-agents__summary-instr { margin: 10px 0 0; font-size: 13px; color: var( --os-ui-fg-muted, #646970 ); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } /* Identity chips — role, abilities, silhouette, hue — all wear the instrument voice: these are facts about the character, stamped rather than said. */ .dm-agents__chips os-chip, .dm-agents__portrait-chips os-chip { --os-ui-chip-font-size: 10.5px; --os-ui-chip-font-weight: 500; --os-ui-chip-bg: transparent; --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 ); --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) ); font-family: var( --os-ui-font-mono, ui-monospace, monospace ); text-transform: uppercase; letter-spacing: 0.06em; } @media ( max-width: 640px ) { .dm-agents__meet, .dm-agents__summary { grid-template-columns: 1fr; } .dm-agents__portrait-face { width: 128px; height: 128px; } } /* The way back to the grid. The window's own breadcrumb goes UP to the folder, which is a different journey: this one returns to the cast. */ .dm-agents__back { align-self: flex-start; margin-block-end: 4px; } /* * Long press is the touch right-click (`parts/long-press.ts`). What * the browser would do with a held finger — iOS's callout, a text * selection creeping across a label — is turned off on the pressable * surfaces, and only where a finger is the pointer: on a desk the * labels stay selectable. */ @media ( pointer: coarse ) { .os-mywp__tile, .os-mywp__cell, .os-mywp__row, .os-mywp__canvas { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; } } html[data-os-mode="mobile"] .os-mywp__tile, html[data-os-mode="mobile"] .os-mywp__cell, html[data-os-mode="mobile"] .os-mywp__row, html[data-os-mode="mobile"] .os-mywp__canvas { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }